1 /*
2  * DHCP library functions.
3  * Copyright (C) 2003, 2004, 2005, 2006 Mondru AB.
4  * Copyright (c) 2006-2008 David Bird <david@coova.com>
5  *
6  * The contents of this file may be used under the terms of the GNU
7  * General Public License Version 2, provided that the above copyright
8  * notice and this permission notice is included in all copies or
9  * substantial portions of the software.
10  *
11  */
12 
13 #include "system.h"
14 #include "syserr.h"
15 #include "radius.h"
16 #include "radius_wispr.h"
17 #include "radius_chillispot.h"
18 #include "redir.h"
19 #include "md5.h"
20 #include "dhcp.h"
21 #include "dns.h"
22 #include "tun.h"
23 #include "chilli.h"
24 #include "options.h"
25 #include "ippool.h"
26 #include "lookup.h"
27 
28 const uint32_t DHCP_OPTION_MAGIC      =  0x63825363;
29 
30 #ifdef NAIVE
31 const static int paranoid = 0; /* Trust that the program has no bugs */
32 #else
33 const static int paranoid = 0; /* Check for errors which cannot happen */
34 #endif
35 
36 extern time_t mainclock;
37 
dhcp_state2name(int authstate)38 char *dhcp_state2name(int authstate) {
39   switch(authstate) {
40   case DHCP_AUTH_NONE: return "none";
41   case DHCP_AUTH_DROP: return "drop";
42   case DHCP_AUTH_PASS: return "pass";
43   case DHCP_AUTH_DNAT: return "dnat";
44   case DHCP_AUTH_SPLASH: return "splash";
45   default: return "unknown";
46   }
47 }
48 
dhcp_list(struct dhcp_t * this,bstring s,bstring pre,bstring post,int listfmt)49 void dhcp_list(struct dhcp_t *this, bstring s, bstring pre, bstring post, int listfmt) {
50   struct dhcp_conn_t *conn = this->firstusedconn;
51   if (listfmt == LIST_JSON_FMT) {
52     bcatcstr(s, "{ \"sessions\":[");
53   }
54   while (conn) {
55     if (pre) bconcat(s, pre);
56     dhcp_print(this, s, listfmt, conn);
57     if (post) bconcat(s, post);
58     conn = conn->next;
59   }
60   if (listfmt == LIST_JSON_FMT) {
61     bcatcstr(s, "]}");
62   }
63 }
64 
dhcp_print(struct dhcp_t * this,bstring s,int listfmt,struct dhcp_conn_t * conn)65 void dhcp_print(struct dhcp_t *this, bstring s, int listfmt, struct dhcp_conn_t *conn) {
66   struct app_conn_t *appconn = (struct app_conn_t *)conn->peer;
67   bstring b = bfromcstr("");
68   bstring tmp = bfromcstr("");
69 
70   if (conn && conn->inuse) {
71 
72     if (listfmt == LIST_JSON_FMT) {
73 
74       if (conn != this->firstusedconn)
75 	bcatcstr(b, ",");
76 
77       bcatcstr(b, "{");
78 
79       if (appconn) {
80 	bcatcstr(b, "\"nasPort\":");
81 	bassignformat(tmp, "%d", appconn->unit);
82 	bconcat(b, tmp);
83 	bcatcstr(b, ",\"clientState\":");
84 	bassignformat(tmp, "%d", appconn->s_state.authenticated);
85 	bconcat(b, tmp);
86 	bcatcstr(b, ",\"macAddress\":\"");
87 	bassignformat(tmp, "%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",
88 		      conn->hismac[0], conn->hismac[1], conn->hismac[2],
89 		      conn->hismac[3], conn->hismac[4], conn->hismac[5]);
90 	bconcat(b, tmp);
91 	bcatcstr(b, "\",\"ipAddress\":\"");
92 	bcatcstr(b, inet_ntoa(conn->hisip));
93 	bcatcstr(b, "\"");
94       }
95 
96     } else {
97 
98       bassignformat(b, "%.2X-%.2X-%.2X-%.2X-%.2X-%.2X %s %s",
99 		    conn->hismac[0], conn->hismac[1], conn->hismac[2],
100 		    conn->hismac[3], conn->hismac[4], conn->hismac[5],
101 		    inet_ntoa(conn->hisip), dhcp_state2name(conn->authstate));
102 
103     }
104 
105     if (listfmt && this->cb_getinfo)
106       this->cb_getinfo(conn, b, listfmt);
107 
108     if (listfmt == LIST_JSON_FMT)
109       bcatcstr(b, "}");
110     else
111       bcatcstr(b, "\n");
112 
113     bconcat(s, b);
114   }
115 
116   bdestroy(b);
117   bdestroy(tmp);
118 }
119 
dhcp_release_mac(struct dhcp_t * this,uint8_t * hwaddr,int term_cause)120 void dhcp_release_mac(struct dhcp_t *this, uint8_t *hwaddr, int term_cause) {
121   struct dhcp_conn_t *conn;
122   if (!dhcp_hashget(this, &conn, hwaddr)) {
123     dhcp_freeconn(conn, term_cause);
124   }
125 }
126 
dhcp_send(struct dhcp_t * this,struct _net_interface * netif,unsigned char * hismac,void * packet,size_t length)127 int dhcp_send(struct dhcp_t *this, struct _net_interface *netif,
128 	      unsigned char *hismac, void *packet, size_t length) {
129 #if defined(__linux__)
130   struct sockaddr_ll dest;
131 
132   memset(&dest, 0, sizeof(dest));
133   dest.sll_family = AF_PACKET;
134   dest.sll_protocol = htons(netif->protocol);
135   dest.sll_ifindex = netif->ifindex;
136   dest.sll_halen = PKT_ETH_ALEN;
137   memcpy(dest.sll_addr, hismac, PKT_ETH_ALEN);
138 
139   if (sendto(netif->fd, packet, length, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
140 #ifdef ENETDOWN
141     if (errno == ENETDOWN) {
142       net_reopen(netif);
143     }
144 #endif
145 #ifdef ENXIO
146     if (errno == ENXIO) {
147       net_reopen(netif);
148     }
149 #endif
150     log_err(errno, "sendto(fd=%d, len=%d) failed", netif->fd, length);
151     return -1;
152   }
153 #elif defined (__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
154   if (write(netif->fd, packet, length) < 0) {
155     log_err(errno, "write() failed");
156     return -1;
157   }
158 #endif
159   return 0;
160 }
161 
162 
163 /**
164  * dhcp_hash()
165  * Generates a 32 bit hash based on a mac address
166  **/
dhcp_hash(uint8_t * hwaddr)167 uint32_t dhcp_hash(uint8_t *hwaddr) {
168   return lookup(hwaddr, PKT_ETH_ALEN, 0);
169 }
170 
171 
172 /**
173  * dhcp_hashinit()
174  * Initialises hash tables
175  **/
dhcp_hashinit(struct dhcp_t * this,int listsize)176 int dhcp_hashinit(struct dhcp_t *this, int listsize) {
177   /* Determine hashlog */
178   for ((this)->hashlog = 0;
179        ((1 << (this)->hashlog) < listsize);
180        (this)->hashlog++);
181 
182   /* Determine hashsize */
183   (this)->hashsize = 1 << (this)->hashlog;
184   (this)->hashmask = (this)->hashsize -1;
185 
186   /* Allocate hash table */
187   if (!((this)->hash = calloc(sizeof(struct dhcp_conn_t), (this)->hashsize))){
188     /* Failed to allocate memory for hash members */
189     return -1;
190   }
191   return 0;
192 }
193 
194 
195 /**
196  * dhcp_hashadd()
197  * Adds a connection to the hash table
198  **/
dhcp_hashadd(struct dhcp_t * this,struct dhcp_conn_t * conn)199 int dhcp_hashadd(struct dhcp_t *this, struct dhcp_conn_t *conn) {
200   uint32_t hash;
201   struct dhcp_conn_t *p;
202   struct dhcp_conn_t *p_prev = NULL;
203 
204   /* Insert into hash table */
205   hash = dhcp_hash(conn->hismac) & this->hashmask;
206   for (p = this->hash[hash]; p; p = p->nexthash)
207     p_prev = p;
208   if (!p_prev)
209     this->hash[hash] = conn;
210   else
211     p_prev->nexthash = conn;
212   return 0; /* Always OK to insert */
213 }
214 
215 
216 /**
217  * dhcp_hashdel()
218  * Removes a connection from the hash table
219  **/
dhcp_hashdel(struct dhcp_t * this,struct dhcp_conn_t * conn)220 int dhcp_hashdel(struct dhcp_t *this, struct dhcp_conn_t *conn) {
221   uint32_t hash;
222   struct dhcp_conn_t *p;
223   struct dhcp_conn_t *p_prev = NULL;
224 
225   /* Find in hash table */
226   hash = dhcp_hash(conn->hismac) & this->hashmask;
227   for (p = this->hash[hash]; p; p = p->nexthash) {
228     if (p == conn) {
229       break;
230     }
231     p_prev = p;
232   }
233 
234   if ((paranoid) && (p!= conn)) {
235     log_err(0, "Tried to delete connection not in hash table");
236   }
237 
238   if (!p_prev)
239     this->hash[hash] = p->nexthash;
240   else
241     p_prev->nexthash = p->nexthash;
242 
243   return 0;
244 }
245 
246 
247 /**
248  * dhcp_hashget()
249  * Uses the hash tables to find a connection based on the mac address.
250  * Returns -1 if not found.
251  **/
dhcp_hashget(struct dhcp_t * this,struct dhcp_conn_t ** conn,uint8_t * hwaddr)252 int dhcp_hashget(struct dhcp_t *this, struct dhcp_conn_t **conn,
253 		 uint8_t *hwaddr) {
254   struct dhcp_conn_t *p;
255   uint32_t hash;
256 
257   /* Find in hash table */
258   hash = dhcp_hash(hwaddr) & this->hashmask;
259   for (p = this->hash[hash]; p; p = p->nexthash) {
260     if ((!memcmp(p->hismac, hwaddr, PKT_ETH_ALEN)) && (p->inuse)) {
261       *conn = p;
262       return 0;
263     }
264   }
265   *conn = NULL;
266   return -1; /* Address could not be found */
267 }
268 
269 
270 /**
271  * dhcp_validate()
272  * Valides reference structures of connections.
273  * Returns the number of active connections
274  **/
dhcp_validate(struct dhcp_t * this)275 int dhcp_validate(struct dhcp_t *this)
276 {
277   int used = 0;
278   int unused = 0;
279   struct dhcp_conn_t *conn;
280   struct dhcp_conn_t *hash_conn;
281 
282   /* Count the number of used connections */
283   conn = this->firstusedconn;
284   while (conn) {
285 
286     if (!conn->inuse) {
287       log_err(0, "Connection with inuse == 0!");
288     }
289 
290     dhcp_hashget(this, &hash_conn, conn->hismac);
291 
292     if (conn != hash_conn) {
293       log_err(0, "Connection could not be found by hashget!");
294     }
295 
296     used ++;
297     conn = conn->next;
298   }
299 
300   /* Count the number of unused connections */
301   conn = this->firstfreeconn;
302   while (conn) {
303     if (conn->inuse) {
304       log_err(0, "Connection with inuse != 0!");
305     }
306     unused ++;
307     conn = conn->next;
308   }
309 
310   if (this->numconn != (used + unused)) {
311     log_err(0, "The number of free and unused connections does not match!");
312     if (this->debug) {
313       log_dbg("used %d unused %d", used, unused);
314       conn = this->firstusedconn;
315       while (conn) {
316 	log_dbg("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
317 	       conn->hismac[0], conn->hismac[1], conn->hismac[2],
318 	       conn->hismac[3], conn->hismac[4], conn->hismac[5]);
319 	conn = conn->next;
320       }
321     }
322   }
323 
324   return used;
325 }
326 
327 
328 /**
329  * dhcp_initconn()
330  * Initialises connection references
331  **/
dhcp_initconn(struct dhcp_t * this)332 int dhcp_initconn(struct dhcp_t *this)
333 {
334   int n;
335   this->firstusedconn = NULL; /* Redundant */
336   this->lastusedconn  = NULL; /* Redundant */
337 
338   for (n=0; n<this->numconn; n++) {
339     this->conn[n].inuse = 0; /* Redundant */
340     if (n == 0) {
341       this->conn[n].prev = NULL; /* Redundant */
342       this->firstfreeconn = &this->conn[n];
343 
344     }
345     else {
346       this->conn[n].prev = &this->conn[n-1];
347       this->conn[n-1].next = &this->conn[n];
348     }
349     if (n == (this->numconn-1)) {
350       this->conn[n].next = NULL; /* Redundant */
351       this->lastfreeconn  = &this->conn[n];
352     }
353   }
354 
355   if (paranoid) dhcp_validate(this);
356 
357   return 0;
358 }
359 
360 /**
361  * dhcp_newconn()
362  * Allocates a new connection from the pool.
363  * Returns -1 if unsuccessful.
364  **/
dhcp_newconn(struct dhcp_t * this,struct dhcp_conn_t ** conn,uint8_t * hwaddr)365 int dhcp_newconn(struct dhcp_t *this,
366 		 struct dhcp_conn_t **conn,
367 		 uint8_t *hwaddr)
368 {
369 
370   if (this->debug)
371     log_dbg("DHCP newconn: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
372 	    hwaddr[0], hwaddr[1], hwaddr[2],
373 	    hwaddr[3], hwaddr[4], hwaddr[5]);
374 
375 
376   if (!this->firstfreeconn) {
377     log_err(0, "Out of free connections");
378     return -1;
379   }
380 
381   *conn = this->firstfreeconn;
382 
383   /* Remove from link of free */
384   if (this->firstfreeconn->next) {
385     this->firstfreeconn->next->prev = NULL;
386     this->firstfreeconn = this->firstfreeconn->next;
387   }
388   else { /* Took the last one */
389     this->firstfreeconn = NULL;
390     this->lastfreeconn = NULL;
391   }
392 
393   /* Initialise structures */
394   memset(*conn, 0, sizeof(**conn));
395 
396   /* Insert into link of used */
397   if (this->firstusedconn) {
398     this->firstusedconn->prev = *conn;
399     (*conn)->next = this->firstusedconn;
400   }
401   else { /* First insert */
402     this->lastusedconn = *conn;
403   }
404 
405   this->firstusedconn = *conn;
406 
407   (*conn)->inuse = 1;
408   (*conn)->parent = this;
409 
410   /* Application specific initialisations */
411   memcpy((*conn)->hismac, hwaddr, PKT_ETH_ALEN);
412   memcpy((*conn)->ourmac, this->ipif.hwaddr, PKT_ETH_ALEN);
413   (*conn)->lasttime = mainclock;
414 
415   dhcp_hashadd(this, *conn);
416 
417   if (paranoid) dhcp_validate(this);
418 
419   /* Inform application that connection was created */
420   if (this->cb_connect)
421     this->cb_connect(*conn);
422 
423   return 0; /* Success */
424 }
425 
426 
427 /**
428  * dhcp_freeconn()
429  * Returns a connection to the pool.
430  **/
dhcp_freeconn(struct dhcp_conn_t * conn,int term_cause)431 int dhcp_freeconn(struct dhcp_conn_t *conn, int term_cause)
432 {
433   /* TODO: Always returns success? */
434 
435   struct dhcp_t *this = conn->parent;
436 
437   /* Tell application that we disconnected */
438   if (this->cb_disconnect)
439     this->cb_disconnect(conn, term_cause);
440 
441   if (this->debug)
442     log_dbg("DHCP freeconn: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
443 	    conn->hismac[0], conn->hismac[1], conn->hismac[2],
444 	    conn->hismac[3], conn->hismac[4], conn->hismac[5]);
445 
446 
447   /* Application specific code */
448   /* First remove from hash table */
449   dhcp_hashdel(this, conn);
450 
451   /* Remove from link of used */
452   if ((conn->next) && (conn->prev)) {
453     conn->next->prev = conn->prev;
454     conn->prev->next = conn->next;
455   }
456   else if (conn->next) { /* && prev == 0 */
457     conn->next->prev = NULL;
458     this->firstusedconn = conn->next;
459   }
460   else if (conn->prev) { /* && next == 0 */
461     conn->prev->next = NULL;
462     this->lastusedconn = conn->prev;
463   }
464   else { /* if ((next == 0) && (prev == 0)) */
465     this->firstusedconn = NULL;
466     this->lastusedconn = NULL;
467   }
468 
469   /* Initialise structures */
470   memset(conn, 0, sizeof(*conn));
471 
472   /* Insert into link of free */
473   if (this->firstfreeconn) {
474     this->firstfreeconn->prev = conn;
475   }
476   else { /* First insert */
477     this->lastfreeconn = conn;
478   }
479 
480   conn->next = this->firstfreeconn;
481   this->firstfreeconn = conn;
482 
483   if (paranoid) dhcp_validate(this);
484 
485   return 0;
486 }
487 
488 
489 /**
490  * dhcp_checkconn()
491  * Checks connections to see if the lease has expired
492  **/
dhcp_checkconn(struct dhcp_t * this)493 int dhcp_checkconn(struct dhcp_t *this)
494 {
495   struct dhcp_conn_t *conn;
496   time_t now = mainclock;
497 
498   now -= this->lease;
499   conn = this->firstusedconn;
500   while (conn) {
501     if (now > conn->lasttime) {
502       if (this->debug)
503 	log_dbg("DHCP timeout: Removing connection");
504       dhcp_freeconn(conn, RADIUS_TERMINATE_CAUSE_LOST_CARRIER);
505       return 0; /* Returning after first deletion */
506     }
507     conn = conn->next;
508   }
509   return 0;
510 }
511 
512 /**
513  * dhcp_new()
514  * Allocates a new instance of the library
515  **/
516 
517 int
dhcp_new(struct dhcp_t ** pdhcp,int numconn,char * interface,int usemac,uint8_t * mac,int promisc,struct in_addr * listen,int lease,int allowdyn,struct in_addr * uamlisten,uint16_t uamport,int useeapol)518 dhcp_new(struct dhcp_t **pdhcp, int numconn, char *interface,
519 	 int usemac, uint8_t *mac, int promisc,
520 	 struct in_addr *listen, int lease, int allowdyn,
521 	 struct in_addr *uamlisten, uint16_t uamport, int useeapol) {
522   struct dhcp_t *dhcp;
523 
524   if (!(dhcp = *pdhcp = calloc(sizeof(struct dhcp_t), 1))) {
525     log_err(0, "calloc() failed");
526     return -1;
527   }
528 
529   dhcp->numconn = numconn;
530 
531   if (!(dhcp->conn = calloc(sizeof(struct dhcp_conn_t), numconn))) {
532     log_err(0, "calloc() failed");
533     free(dhcp);
534     return -1;
535   }
536 
537   dhcp_initconn(dhcp);
538 
539   if (net_init(&dhcp->ipif, interface, PKT_ETH_PROTO_IP, promisc, usemac ? mac : 0) < 0) {
540     free(dhcp->conn);
541     free(dhcp);
542     return -1;
543   }
544 
545 #if defined (__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__)
546   {
547     int blen=0;
548     if (ioctl(dhcp->ipif.fd, BIOCGBLEN, &blen) < 0) {
549       log_err(errno,"ioctl() failed!");
550     }
551     dhcp->rbuf_max = blen;
552     if (!(dhcp->rbuf = calloc(dhcp->rbuf_max, 1))) {
553       /* TODO: Free malloc */
554       log_err(errno, "malloc() failed");
555     }
556     dhcp->rbuf_offset = 0;
557     dhcp->rbuf_len = 0;
558   }
559 #endif
560 
561   if (net_init(&dhcp->arpif, interface, PKT_ETH_PROTO_ARP, promisc, usemac ? mac : 0) < 0) {
562       close(dhcp->ipif.fd);
563       free(dhcp->conn);
564       free(dhcp);
565       return -1; /* Error reporting done in dhcp_open_eth */
566     }
567 
568   if (useeapol) {
569     if (net_init(&dhcp->eapif, interface, PKT_ETH_PROTO_EAPOL, promisc, usemac ? mac : 0) < 0) {
570       close(dhcp->ipif.fd);
571       close(dhcp->arpif.fd);
572       free(dhcp->conn);
573       free(dhcp);
574       return -1; /* Error reporting done in eapol_open_eth */
575     }
576   }
577 
578   if (options.dhcpgwip.s_addr != 0) {
579     int fd;
580     struct sockaddr_in addr;
581     int on = 1;
582 
583     memset(&addr, 0, sizeof(addr));
584     addr.sin_family = AF_INET;
585     addr.sin_addr.s_addr = htonl(INADDR_ANY);
586     addr.sin_port = htons(68);
587 
588     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
589 	bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
590       log_err(errno, "socket or bind failed for dhcp relay!");
591       close(dhcp->ipif.fd);
592       close(dhcp->arpif.fd);
593       close(dhcp->eapif.fd);
594       free(dhcp->conn);
595       free(dhcp);
596       close(fd);
597       return -1;
598     }
599 
600     if (setsockopt(dhcp->relayfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
601       log_err(errno, "Can't set reuse option");
602     }
603 
604     dhcp->relayfd = fd;
605   }
606 
607   if (dhcp_hashinit(dhcp, dhcp->numconn))
608     return -1; /* Failed to allocate hash tables */
609 
610   /* Initialise various variables */
611   dhcp->ourip.s_addr = listen->s_addr;
612   dhcp->lease = lease;
613   dhcp->allowdyn = allowdyn;
614   dhcp->uamlisten.s_addr = uamlisten->s_addr;
615   dhcp->uamport = uamport;
616 
617   /* Initialise call back functions */
618   dhcp->cb_data_ind = 0;
619   dhcp->cb_eap_ind = 0;
620   dhcp->cb_request = 0;
621   dhcp->cb_disconnect = 0;
622   dhcp->cb_connect = 0;
623 
624   return 0;
625 }
626 
627 /**
628  * dhcp_set()
629  * Set dhcp parameters which can be altered at runtime.
630  **/
631 int
dhcp_set(struct dhcp_t * dhcp,int debug)632 dhcp_set(struct dhcp_t *dhcp, int debug) {
633   dhcp->debug = debug;
634   dhcp->anydns = options.uamanydns;
635 
636   /* Copy list of uamserver IP addresses */
637   if (dhcp->authip) free(dhcp->authip);
638   dhcp->authiplen = options.uamserverlen;
639 
640   if (!(dhcp->authip = calloc(sizeof(struct in_addr), options.uamserverlen))) {
641     log_err(0, "calloc() failed");
642     dhcp->authip = 0;
643     return -1;
644   }
645 
646   memcpy(dhcp->authip, &options.uamserver, sizeof(struct in_addr) * options.uamserverlen);
647 
648   return 0;
649 }
650 
651 /**
652  * dhcp_free()
653  * Releases ressources allocated to the instance of the library
654  **/
dhcp_free(struct dhcp_t * dhcp)655 int dhcp_free(struct dhcp_t *dhcp) {
656   if (dhcp->hash) free(dhcp->hash);
657   if (dhcp->authip) free(dhcp->authip);
658   dev_set_flags(dhcp->ipif.devname, dhcp->ipif.devflags);
659   net_close(&dhcp->ipif);
660   net_close(&dhcp->arpif);
661   net_close(&dhcp->eapif);
662   free(dhcp->conn);
663   free(dhcp);
664   return 0;
665 }
666 
667 /**
668  * dhcp_timeout()
669  * Need to call this function at regular intervals to clean up old connections.
670  **/
671 int
dhcp_timeout(struct dhcp_t * this)672 dhcp_timeout(struct dhcp_t *this)
673 {
674   if (paranoid)
675     dhcp_validate(this);
676 
677   dhcp_checkconn(this);
678 
679   return 0;
680 }
681 
682 /**
683  * dhcp_timeleft()
684  * Use this function to find out when to call dhcp_timeout()
685  * If service is needed after the value given by tvp then tvp
686  * is left unchanged.
687  **/
dhcp_timeleft(struct dhcp_t * this,struct timeval * tvp)688 struct timeval* dhcp_timeleft(struct dhcp_t *this, struct timeval *tvp) {
689   return tvp;
690 }
691 
check_garden(pass_through * ptlist,int ptcnt,struct pkt_ippacket_t * pack,int dst)692 int check_garden(pass_through *ptlist, int ptcnt, struct pkt_ippacket_t *pack, int dst) {
693   struct pkt_tcphdr_t *tcph = (struct pkt_tcphdr_t *)pack->payload;
694   struct pkt_udphdr_t *udph = (struct pkt_udphdr_t *)pack->payload;
695   pass_through *pt;
696   int i;
697 
698   for (i = 0; i < ptcnt; i++) {
699     pt = &ptlist[i];
700     if (pt->proto == 0 || pack->iph.protocol == pt->proto)
701       if (pt->host.s_addr == 0 ||
702 	  pt->host.s_addr == ((dst ? pack->iph.daddr : pack->iph.saddr) & pt->mask.s_addr))
703 	if (pt->port == 0 ||
704 	    (pack->iph.protocol == PKT_IP_PROTO_TCP && (dst ? tcph->dst : tcph->src) == htons(pt->port)) ||
705 	    (pack->iph.protocol == PKT_IP_PROTO_UDP && (dst ? udph->dst : udph->src) == htons(pt->port)))
706 	  return 1;
707   }
708 
709   return 0;
710 }
711 
712 static
dhcp_nakDNS(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t len)713 int dhcp_nakDNS(struct dhcp_conn_t *conn, struct pkt_ippacket_t *pack, size_t len) {
714   struct dhcp_t *this = conn->parent;
715   struct pkt_udphdr_t *udph = (struct pkt_udphdr_t *)pack->payload;
716   /*struct dns_packet_t *dnsp = (struct dns_packet_t *)((char*)pack->payload + sizeof(struct pkt_udphdr_t));*/
717   struct dns_fullpacket_t answer;
718 
719   memcpy(&answer, pack, len);
720 
721   /* DNS response, with no host error code */
722   answer.dns.flags = htons(0x8583);
723 
724   /* UDP */
725   answer.udph.src = udph->dst;
726   answer.udph.dst = udph->src;
727 
728   /* IP */
729   answer.iph.check = 0; /* Calculate at end of packet */
730   memcpy(&answer.iph.daddr, &pack->iph.saddr, PKT_IP_ALEN);
731   memcpy(&answer.iph.saddr, &pack->iph.daddr, PKT_IP_ALEN);
732 
733   /* Ethernet */
734   memcpy(&answer.ethh.dst, &pack->ethh.src, PKT_ETH_ALEN);
735   memcpy(&answer.ethh.src, &pack->ethh.dst, PKT_ETH_ALEN);
736   answer.ethh.prot = htons(PKT_ETH_PROTO_IP);
737 
738   /* checksums */
739   chksum(&answer.iph);
740 
741   dhcp_send(this, &this->ipif, conn->hismac, &answer, len);
742 
743   return 0;
744 }
745 
746 static
_filterDNSreq(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t plen)747 int _filterDNSreq(struct dhcp_conn_t *conn, struct pkt_ippacket_t *pack, size_t plen) {
748   /*struct dhcp_udphdr_t *udph = (struct dhcp_udphdr_t*)pack->payload;*/
749   struct dns_packet_t *dnsp = (struct dns_packet_t *)((char*)pack->payload + sizeof(struct pkt_udphdr_t));
750   size_t len = plen - DHCP_DNS_HLEN - PKT_UDP_HLEN - PKT_IP_HLEN - PKT_ETH_HLEN;
751   size_t olen = len;
752 
753   uint16_t id = ntohs(dnsp->id);
754   uint16_t flags = ntohs(dnsp->flags);
755   uint16_t qdcount = ntohs(dnsp->qdcount);
756   uint16_t ancount = ntohs(dnsp->ancount);
757   uint16_t nscount = ntohs(dnsp->nscount);
758   uint16_t arcount = ntohs(dnsp->arcount);
759 
760   uint8_t *p_pkt = (uint8_t *)dnsp->records;
761   char q[256];
762 
763   int d = options.debug; /* XXX: debug */
764   int i;
765 
766   if (d) log_dbg("DNS ID:    %d", id);
767   if (d) log_dbg("DNS Flags: %d", flags);
768 
769   /* it was a response? shouldn't be */
770   /*if (((flags & 0x8000) >> 15) == 1) return 0;*/
771 
772   memset(q,0,sizeof(q));
773 
774 #undef  copyres
775 #define copyres(isq,n)			        \
776   if (d) log_dbg(#n ": %d", n ## count);        \
777   for (i=0; i < n ## count; i++)                \
778     if (dns_copy_res(isq, &p_pkt, &len,         \
779 		     (uint8_t *)dnsp, olen, 	\
780                      q, sizeof(q)))	        \
781       return dhcp_nakDNS(conn,pack,plen)
782 
783   copyres(1,qd);
784   copyres(0,an);
785   copyres(0,ns);
786   copyres(0,ar);
787 
788   if (d) log_dbg("left (should be zero): %d", len);
789 
790   return 1;
791 }
792 
793 static
_filterDNSresp(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t plen)794 int _filterDNSresp(struct dhcp_conn_t *conn, struct pkt_ippacket_t *pack, size_t plen) {
795   /*struct dhcp_udphdr_t *udph = (struct dhcp_udphdr_t*)pack->payload;*/
796   struct dns_packet_t *dnsp = (struct dns_packet_t *)((char*)pack->payload + sizeof(struct pkt_udphdr_t));
797   size_t len = plen - DHCP_DNS_HLEN - PKT_UDP_HLEN - PKT_IP_HLEN - PKT_ETH_HLEN;
798   size_t olen = len;
799 
800   uint16_t id = ntohs(dnsp->id);
801   uint16_t flags = ntohs(dnsp->flags);
802   uint16_t qdcount = ntohs(dnsp->qdcount);
803   uint16_t ancount = ntohs(dnsp->ancount);
804   uint16_t nscount = ntohs(dnsp->nscount);
805   uint16_t arcount = ntohs(dnsp->arcount);
806 
807   uint8_t *p_pkt = (uint8_t *)dnsp->records;
808   char q[256];
809 
810   int d = options.debug; /* XXX: debug */
811   int i;
812 
813   if (d) log_dbg("DNS ID:    %d", id);
814   if (d) log_dbg("DNS Flags: %d", flags);
815 
816   /* it was a query? shouldn't be */
817   if (((flags & 0x8000) >> 15) == 0) return 0;
818 
819   memset(q,0,sizeof(q));
820 
821 #undef  copyres
822 #define copyres(isq,n)			        \
823   if (d) log_dbg(#n ": %d", n ## count);        \
824   for (i=0; i < n ## count; i++)                \
825     dns_copy_res(isq, &p_pkt, &len,             \
826 		     (uint8_t *)dnsp, olen, 	\
827                      q, sizeof(q))
828 
829   copyres(1,qd);
830   copyres(0,an);
831   copyres(0,ns);
832   copyres(0,ar);
833 
834   if (d) log_dbg("left (should be zero): %d", len);
835 
836   /*
837   dnsp->flags = htons(flags);
838   dnsp->qdcount = htons(qdcount);
839   dnsp->ancount = htons(ancount);
840   dnsp->nscount = htons(nscount);
841   dnsp->arcount = htons(arcount);
842   */
843 
844   return 1;
845 }
846 
847 
848 /**
849  * dhcp_doDNAT()
850  * Change destination address to authentication server.
851  **/
dhcp_doDNAT(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t len)852 int dhcp_doDNAT(struct dhcp_conn_t *conn,
853 		struct pkt_ippacket_t *pack, size_t len) {
854   struct dhcp_t *this = conn->parent;
855   struct pkt_tcphdr_t *tcph = (struct pkt_tcphdr_t *)pack->payload;
856   struct pkt_udphdr_t *udph = (struct pkt_udphdr_t *)pack->payload;
857   int i;
858 
859   /* Allow localhost through network... */
860   if (pack->iph.daddr == INADDR_LOOPBACK)
861     return 0;
862 
863   /* Was it an ICMP request for us? */
864   if (pack->iph.protocol == PKT_IP_PROTO_ICMP)
865     if (pack->iph.daddr == conn->ourip.s_addr)
866       return 0;
867 
868   /* Was it a DNS request? */
869   if (((this->anydns) ||
870        (pack->iph.daddr == conn->dns1.s_addr) ||
871        (pack->iph.daddr == conn->dns2.s_addr)) &&
872       (pack->iph.protocol == PKT_IP_PROTO_UDP && udph->dst == htons(DHCP_DNS))) {
873     if (options.dnsparanoia) {
874       if (_filterDNSreq(conn, pack, len))
875 	return 0;
876       else /* drop */
877 	return -1;
878     } else { /* allow */
879       return 0;
880     }
881   }
882 
883   /* Was it a request for authentication server? */
884   for (i = 0; i<this->authiplen; i++) {
885     if ((pack->iph.daddr == this->authip[i].s_addr) /* &&
886 	(pack->iph.protocol == PKT_IP_PROTO_TCP) &&
887 	((tcph->dst == htons(DHCP_HTTP)) ||
888 	(tcph->dst == htons(DHCP_HTTPS)))*/)
889       return 0; /* Destination was authentication server */
890   }
891 
892   /* Was it a request for local redirection server? */
893   if ((pack->iph.daddr == this->uamlisten.s_addr) &&
894       (pack->iph.protocol == PKT_IP_PROTO_TCP) &&
895       (tcph->dst == htons(this->uamport)))
896     return 0; /* Destination was local redir server */
897 
898   /* Was it a request for a pass-through entry? */
899   if (check_garden(options.pass_throughs, options.num_pass_throughs, pack, 1))
900     return 0;
901   /* Check uamdomain driven walled garden */
902   if (check_garden(this->pass_throughs, this->num_pass_throughs, pack, 1))
903     return 0;
904 
905   /* Check appconn session specific pass-throughs */
906   if (conn->peer) {
907     struct app_conn_t *appconn = (struct app_conn_t *)conn->peer;
908     if (check_garden(appconn->s_params.pass_throughs, appconn->s_params.pass_through_count, pack, 1))
909       return 0;
910   }
911 
912   /* Was it a http request for another server? */
913   /* We are changing dest IP and dest port to local UAM server */
914   if ((pack->iph.protocol == PKT_IP_PROTO_TCP) &&
915       (tcph->dst == htons(DHCP_HTTP))) {
916     int n;
917     int pos=-1;
918 
919     for (n=0; n<DHCP_DNAT_MAX; n++) {
920       if ((conn->dnatip[n] == pack->iph.daddr) &&
921 	  (conn->dnatport[n] == tcph->src)) {
922 	pos = n;
923 	break;
924       }
925     }
926     if (pos==-1) { /* Save for undoing */
927       if (options.usetap)
928 	memcpy(conn->dnatmac[conn->nextdnat], pack->ethh.dst, PKT_ETH_ALEN);
929       conn->dnatip[conn->nextdnat] = pack->iph.daddr;
930       conn->dnatport[conn->nextdnat] = tcph->src;
931       conn->nextdnat = (conn->nextdnat + 1) % DHCP_DNAT_MAX;
932     }
933 
934     if (options.usetap)
935       memcpy(pack->ethh.dst, tuntap(tun).hwaddr, PKT_ETH_ALEN);
936 
937     pack->iph.daddr = this->uamlisten.s_addr;
938     tcph->dst = htons(this->uamport);
939 
940     chksum(&pack->iph);
941     return 0;
942   }
943 
944   return -1; /* Something else */
945 
946 }
947 
dhcp_postauthDNAT(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t len,int isreturn)948 int dhcp_postauthDNAT(struct dhcp_conn_t *conn, struct pkt_ippacket_t *pack, size_t len, int isreturn) {
949   struct dhcp_t *this = conn->parent;
950   struct pkt_tcphdr_t *tcph = (struct pkt_tcphdr_t *)pack->payload;
951   /*struct pkt_udphdr_t *udph = (struct pkt_udphdr_t *)pack->payload;*/
952 
953   if (options.postauth_proxyport > 0) {
954     if (isreturn) {
955       if ((pack->iph.protocol == PKT_IP_PROTO_TCP) &&
956 	  (pack->iph.saddr == options.postauth_proxyip.s_addr) &&
957 	  (tcph->src == htons(options.postauth_proxyport))) {
958 	int n;
959 	for (n=0; n<DHCP_DNAT_MAX; n++) {
960 	  if (tcph->dst == conn->dnatport[n]) {
961 	    if (options.usetap)
962 	      memcpy(pack->ethh.src, conn->dnatmac[n], PKT_ETH_ALEN);
963 	    pack->iph.saddr = conn->dnatip[n];
964 	    tcph->src = htons(DHCP_HTTP);
965 
966 	    chksum(&pack->iph);
967 
968 	    return 0; /* It was a DNAT reply */
969 	  }
970 	}
971 	return 0;
972       }
973     }
974     else {
975       if ((pack->iph.protocol == PKT_IP_PROTO_TCP) &&
976 	  (tcph->dst == htons(DHCP_HTTP))) {
977 
978 	int n;
979 	int pos=-1;
980 
981 	for (n = 0; n<this->authiplen; n++)
982 	  if ((pack->iph.daddr == this->authip[n].s_addr))
983 	      return 0;
984 
985 	for (n=0; n<DHCP_DNAT_MAX; n++) {
986 	  if ((conn->dnatip[n] == pack->iph.daddr) &&
987 	      (conn->dnatport[n] == tcph->src)) {
988 	    pos = n;
989 	    break;
990 	  }
991 	}
992 
993 	if (pos==-1) { /* Save for undoing */
994 	  if (options.usetap)
995 	    memcpy(conn->dnatmac[conn->nextdnat], pack->ethh.dst, PKT_ETH_ALEN);
996 	  conn->dnatip[conn->nextdnat] = pack->iph.daddr;
997 	  conn->dnatport[conn->nextdnat] = tcph->src;
998 	  conn->nextdnat = (conn->nextdnat + 1) % DHCP_DNAT_MAX;
999 	}
1000 
1001 	log_dbg("rewriting packet for post-auth proxy %s:%d",
1002 		inet_ntoa(options.postauth_proxyip),
1003 		options.postauth_proxyport);
1004 
1005 	pack->iph.daddr = options.postauth_proxyip.s_addr;
1006 	tcph->dst = htons(options.postauth_proxyport);
1007 
1008 	chksum(&pack->iph);
1009 
1010 	return 0;
1011       }
1012     }
1013   }
1014 
1015   return -1; /* Something else */
1016 }
1017 
1018 /**
1019  * dhcp_undoDNAT()
1020  * Change source address back to original server
1021  **/
dhcp_undoDNAT(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t * plen)1022 int dhcp_undoDNAT(struct dhcp_conn_t *conn, struct pkt_ippacket_t *pack, size_t *plen) {
1023   struct dhcp_t *this = conn->parent;
1024   struct pkt_tcphdr_t *tcph = (struct pkt_tcphdr_t *)pack->payload;
1025   struct pkt_udphdr_t *udph = (struct pkt_udphdr_t *)pack->payload;
1026   /*size_t len = *plen;*/
1027   int i;
1028 
1029   /* Allow localhost through network... */
1030   if (pack->iph.saddr == INADDR_LOOPBACK)
1031     return 0;
1032 
1033   /* Was it a DNS reply? */
1034   if (((this->anydns) ||
1035        (pack->iph.saddr == conn->dns1.s_addr) ||
1036        (pack->iph.saddr == conn->dns2.s_addr)) &&
1037       (pack->iph.protocol == PKT_IP_PROTO_UDP && udph->src == htons(DHCP_DNS))) {
1038     if (options.uamdomains) {
1039 	if (_filterDNSresp(conn, pack, *plen))
1040 	  return 0;
1041 	else
1042 	  return -1; /* drop */
1043     } else {   /* always let through dns when not filtering */
1044       return 0;
1045     }
1046   }
1047 
1048   if (pack->iph.protocol == PKT_IP_PROTO_ICMP) {
1049     /* Was it an ICMP reply from us? */
1050     if (pack->iph.saddr == conn->ourip.s_addr)
1051       return 0;
1052     /* Allow for MTU negotiation */
1053     if (options.debug)
1054       log_dbg("Received ICMP type=%d code=%d",
1055 	      (int)pack->payload[0],(int)pack->payload[1]);
1056     switch((unsigned char)pack->payload[0]) {
1057     case 0:  /* echo reply */
1058     case 3:  /* destination unreachable */
1059     case 5:  /* redirect */
1060     case 11: /* time excedded */
1061       switch((unsigned char)pack->payload[1]) {
1062       case 4:
1063 	log(LOG_NOTICE, "Fragmentation needed ICMP");
1064       }
1065       if (options.debug)
1066 	log_dbg("Forwarding ICMP to chilli client");
1067       return 0;
1068     }
1069     /* fail all else */
1070     return -1;
1071   }
1072 
1073   /*
1074 12:46:20.767600 IP 10.1.0.1.49335 > 68.142.197.198.80: S 1442428713:1442428713(0) win 65535 <mss 1460,sackOK,eol>
1075 12:46:20.768234 IP 10.1.0.10.3990 > 10.1.0.1.49335: S 746818639:746818639(0) ack 1442428714 win 5840 <mss 1460,nop,nop,sackOK>
1076   */
1077 
1078   /* Was it a reply from redir server? */
1079   if ((pack->iph.saddr == this->uamlisten.s_addr) &&
1080       (pack->iph.protocol == PKT_IP_PROTO_TCP) &&
1081       (tcph->src == htons(this->uamport))) {
1082     int n;
1083 
1084     for (n=0; n<DHCP_DNAT_MAX; n++) {
1085       if (tcph->dst == conn->dnatport[n]) {
1086 
1087 	if (options.usetap)
1088 	  memcpy(pack->ethh.src, conn->dnatmac[n], PKT_ETH_ALEN);
1089 
1090 	pack->iph.saddr = conn->dnatip[n];
1091 	tcph->src = htons(DHCP_HTTP);
1092 
1093 	chksum(&pack->iph);
1094 
1095 	return 0; /* It was a DNAT reply */
1096       }
1097     }
1098     return 0; /* It was a normal reply from redir server */
1099   }
1100 
1101   /* Was it a normal http or https reply from authentication server? */
1102   /* Was it a normal reply from authentication server? */
1103   for (i = 0; i<this->authiplen; i++) {
1104     if ((pack->iph.saddr == this->authip[i].s_addr) /* &&
1105 	(pack->iph.protocol == PKT_IP_PROTO_TCP) &&
1106 	((tcph->src == htons(DHCP_HTTP)) ||
1107 	(tcph->src == htons(DHCP_HTTPS)))*/)
1108       return 0; /* Destination was authentication server */
1109   }
1110 
1111   /* Was it a reply for a pass-through entry? */
1112   if (check_garden(options.pass_throughs, options.num_pass_throughs, pack, 0))
1113     return 0;
1114   if (check_garden(this->pass_throughs, this->num_pass_throughs, pack, 0))
1115     return 0;
1116 
1117   /* Check appconn session specific pass-throughs */
1118   if (conn->peer) {
1119     struct app_conn_t *appconn = (struct app_conn_t *)conn->peer;
1120     if (check_garden(appconn->s_params.pass_throughs, appconn->s_params.pass_through_count, pack, 0))
1121       return 0;
1122   }
1123 
1124   return -1; /* Something else */
1125 }
1126 
1127 /**
1128  * dhcp_checkDNS()
1129  * Check if it was request for known domain name.
1130  * In case it was a request for a known keyword then
1131  * redirect to the login/logout page
1132  * 2005-09-19: This stuff is highly experimental.
1133  **/
dhcp_checkDNS(struct dhcp_conn_t * conn,struct pkt_ippacket_t * pack,size_t len)1134 int dhcp_checkDNS(struct dhcp_conn_t *conn,
1135 		  struct pkt_ippacket_t *pack, size_t len) {
1136 
1137   struct dhcp_t *this = conn->parent;
1138   struct pkt_udphdr_t *udph = (struct pkt_udphdr_t *)pack->payload;
1139   struct dns_packet_t *dnsp = (struct dns_packet_t *)((char*)pack->payload + sizeof(struct pkt_udphdr_t));
1140   struct dns_fullpacket_t answer;
1141   uint8_t *p1 = NULL;
1142   uint8_t *p2 = NULL;
1143   size_t length;
1144   size_t udp_len;
1145   uint8_t query[256];
1146   size_t query_len = 0;
1147   int n;
1148 
1149   log_dbg("DNS ID:    %d", ntohs(dnsp->id));
1150   log_dbg("DNS flags: %d", ntohs(dnsp->flags));
1151 
1152   if ((ntohs(dnsp->flags)   == 0x0100) &&
1153       (ntohs(dnsp->qdcount) == 0x0001) &&
1154       (ntohs(dnsp->ancount) == 0x0000) &&
1155       (ntohs(dnsp->nscount) == 0x0000) &&
1156       (ntohs(dnsp->arcount) == 0x0000)) {
1157 
1158     log_dbg("It was a query %s", dnsp->records);
1159 
1160     p1 = dnsp->records + 1 + dnsp->records[0];
1161     p2 = dnsp->records;
1162 
1163     do {
1164       if (query_len < 256)
1165 	query[query_len++] = *p2;
1166     } while (*p2++ != 0); /* TODO */
1167 
1168     for (n=0; n<4; n++) {
1169       if (query_len < 256)
1170 	query[query_len++] = *p2++;
1171     }
1172 
1173     query[query_len++] = 0xc0;
1174     query[query_len++] = 0x0c;
1175     query[query_len++] = 0x00;
1176     query[query_len++] = 0x01;
1177     query[query_len++] = 0x00;
1178     query[query_len++] = 0x01;
1179     query[query_len++] = 0x00;
1180     query[query_len++] = 0x00;
1181     query[query_len++] = 0x01;
1182     query[query_len++] = 0x2c;
1183     query[query_len++] = 0x00;
1184     query[query_len++] = 0x04;
1185     memcpy(&query[query_len], &conn->ourip.s_addr, 4);
1186     query_len += 4;
1187 
1188     if (!memcmp(p1,
1189 		"\3key\12chillispot\3org",
1190 		sizeof("\3key\12chillispot\3org"))) {
1191       log_dbg("It was a matching query %s: \n", dnsp->records);
1192       memcpy(&answer, pack, len); /* TODO */
1193 
1194       /* DNS Header */
1195       answer.dns.id      = dnsp->id;
1196       answer.dns.flags   = htons(0x8000);
1197       answer.dns.qdcount = htons(0x0001);
1198       answer.dns.ancount = htons(0x0001);
1199       answer.dns.nscount = htons(0x0000);
1200       answer.dns.arcount = htons(0x0000);
1201       memcpy(answer.dns.records, query, query_len);
1202 
1203       /* UDP header */
1204       udp_len = query_len + DHCP_DNS_HLEN + PKT_UDP_HLEN;
1205       answer.udph.len = htons(udp_len);
1206       answer.udph.src = udph->dst;
1207       answer.udph.dst = udph->src;
1208 
1209       /* IP header */
1210       answer.iph.version_ihl = PKT_IP_VER_HLEN;
1211       answer.iph.tos = 0;
1212       answer.iph.tot_len = htons(udp_len + PKT_IP_HLEN);
1213       answer.iph.id = 0;
1214       answer.iph.frag_off = 0;
1215       answer.iph.ttl = 0x10;
1216       answer.iph.protocol = 0x11;
1217       answer.iph.check = 0; /* Calculate at end of packet */
1218       memcpy(&answer.iph.daddr, &pack->iph.saddr, PKT_IP_ALEN);
1219       memcpy(&answer.iph.saddr, &pack->iph.saddr, PKT_IP_ALEN);
1220 
1221       /* Ethernet header */
1222       memcpy(&answer.ethh.dst, &pack->ethh.src, PKT_ETH_ALEN);
1223       memcpy(&answer.ethh.src, &pack->ethh.dst, PKT_ETH_ALEN);
1224       answer.ethh.prot = htons(PKT_ETH_PROTO_IP);
1225 
1226       /* Work out checksums */
1227       chksum(&answer.iph);
1228 
1229       /* Calculate total length */
1230       length = udp_len + PKT_IP_HLEN + PKT_ETH_HLEN;
1231 
1232       return dhcp_send(this, &this->ipif, conn->hismac, &answer, length);
1233     }
1234   }
1235   return -1; /* Something else */
1236 }
1237 
1238 /**
1239  * dhcp_getdefault()
1240  * Fill in a DHCP packet with most essential values
1241  **/
1242 int
dhcp_getdefault(struct dhcp_fullpacket_t * pack)1243 dhcp_getdefault(struct dhcp_fullpacket_t *pack) {
1244 
1245   /* Initialise reply packet with request */
1246   memset(pack, 0, sizeof(struct dhcp_fullpacket_t));
1247 
1248   /* DHCP Payload */
1249   pack->dhcp.op     = DHCP_BOOTREPLY;
1250   pack->dhcp.htype  = DHCP_HTYPE_ETH;
1251   pack->dhcp.hlen   = PKT_ETH_ALEN;
1252 
1253   /* IP header */
1254   pack->iph.version_ihl = PKT_IP_VER_HLEN;
1255   pack->iph.tos = 0;
1256   pack->iph.tot_len = 0; /* Calculate at end of packet */
1257   pack->iph.id = 0;
1258   pack->iph.frag_off = 0;
1259   pack->iph.ttl = 0x10;
1260   pack->iph.protocol = 0x11;
1261   pack->iph.check = 0; /* Calculate at end of packet */
1262 
1263   /* Ethernet header */
1264   pack->ethh.prot = htons(PKT_ETH_PROTO_IP);
1265 
1266   return 0;
1267 }
1268 
1269 /**
1270  * dhcp_create_pkt()
1271  * Create a new typed DHCP packet
1272  */
1273 int
dhcp_create_pkt(uint8_t type,struct dhcp_fullpacket_t * pack,struct dhcp_fullpacket_t * req,struct dhcp_conn_t * conn)1274 dhcp_create_pkt(uint8_t type, struct dhcp_fullpacket_t *pack,
1275 		struct dhcp_fullpacket_t *req, struct dhcp_conn_t *conn) {
1276   struct dhcp_t *this = conn->parent;
1277   int pos = 0;
1278 
1279   dhcp_getdefault(pack);
1280 
1281   pack->dhcp.xid      = req->dhcp.xid;
1282   pack->dhcp.flags[0] = req->dhcp.flags[0];
1283   pack->dhcp.flags[1] = req->dhcp.flags[1];
1284   pack->dhcp.giaddr   = req->dhcp.giaddr;
1285 
1286   memcpy(&pack->dhcp.chaddr, &req->dhcp.chaddr, DHCP_CHADDR_LEN);
1287   memcpy(&pack->dhcp.sname, conn->dhcp_opts.sname, DHCP_SNAME_LEN);
1288   memcpy(&pack->dhcp.file, conn->dhcp_opts.file, DHCP_FILE_LEN);
1289 
1290   log_dbg("!!! dhcp server : %s !!!", pack->dhcp.sname);
1291 
1292   switch(type) {
1293   case DHCPOFFER:
1294     pack->dhcp.yiaddr = conn->hisip.s_addr;
1295     break;
1296   case DHCPACK:
1297     pack->dhcp.xid    = req->dhcp.xid;
1298     pack->dhcp.ciaddr = req->dhcp.ciaddr;
1299     pack->dhcp.yiaddr = conn->hisip.s_addr;
1300     break;
1301   case DHCPNAK:
1302     break;
1303   }
1304 
1305   /* Ethernet Header */
1306   memcpy(pack->ethh.dst, conn->hismac, PKT_ETH_ALEN);
1307   memcpy(pack->ethh.src, this->ipif.hwaddr, PKT_ETH_ALEN);
1308 
1309   /* UDP and IP Headers */
1310   pack->udph.src = htons(DHCP_BOOTPS);
1311   pack->iph.saddr = conn->ourip.s_addr;
1312 
1313   /** http://www.faqs.org/rfcs/rfc1542.html
1314       BOOTREQUEST fields     BOOTREPLY values for UDP, IP, link-layer
1315    +-----------------------+-----------------------------------------+
1316    | 'ciaddr'  'giaddr'  B | UDP dest     IP destination   link dest |
1317    +-----------------------+-----------------------------------------+
1318    | non-zero     X      X | BOOTPC (68)  'ciaddr'         normal    |
1319    | 0.0.0.0   non-zero  X | BOOTPS (67)  'giaddr'         normal    |
1320    | 0.0.0.0   0.0.0.0   0 | BOOTPC (68)  'yiaddr'         'chaddr'  |
1321    | 0.0.0.0   0.0.0.0   1 | BOOTPC (68)  255.255.255.255  broadcast |
1322    +-----------------------+-----------------------------------------+
1323 
1324         B = BROADCAST flag
1325 
1326         X = Don't care
1327 
1328    normal = determine from the given IP destination using normal
1329             IP routing mechanisms and/or ARP as for any other
1330             normal datagram
1331 
1332    If the 'giaddr' field in a DHCP message from a client is non-zero,
1333    the server sends any return messages to the 'DHCP server' port on the
1334    BOOTP relay agent whose address appears in 'giaddr'.
1335 
1336    If the 'giaddr' field is zero and the 'ciaddr' field is nonzero, then the
1337    server unicasts DHCPOFFER and DHCPACK messages to the address in
1338    'ciaddr'.
1339 
1340    If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is set,
1341    then the server broadcasts DHCPOFFER and DHCPACK messages to
1342    0xffffffff.
1343 
1344    If the broadcast bit is not set and 'giaddr' is zero and 'ciaddr' is
1345    zero, then the server unicasts DHCPOFFER and DHCPACK messages to the
1346    client's hardware address and 'yiaddr' address.
1347 
1348    In all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
1349    messages to 0xffffffff.
1350 
1351   **/
1352 
1353   if (req->dhcp.ciaddr) {
1354     pack->iph.daddr = req->dhcp.ciaddr;
1355     pack->udph.dst = htons(DHCP_BOOTPC);
1356   } else if (req->dhcp.giaddr) {
1357     pack->iph.daddr = req->dhcp.giaddr;
1358     pack->udph.dst = htons(DHCP_BOOTPS);
1359   } else if (type == DHCPNAK || req->dhcp.flags[0] & 0x80) {
1360     pack->iph.daddr = ~0;
1361     pack->udph.dst = htons(DHCP_BOOTPC);
1362     pack->dhcp.flags[0] = 0x80;
1363   } else {
1364     pack->iph.daddr = pack->dhcp.yiaddr;
1365     pack->udph.dst = htons(DHCP_BOOTPC);
1366   }
1367 
1368   /* Magic cookie */
1369   pack->dhcp.options[pos++] = 0x63;
1370   pack->dhcp.options[pos++] = 0x82;
1371   pack->dhcp.options[pos++] = 0x53;
1372   pack->dhcp.options[pos++] = 0x63;
1373 
1374   pack->dhcp.options[pos++] = DHCP_OPTION_MESSAGE_TYPE;
1375   pack->dhcp.options[pos++] = 1;
1376   pack->dhcp.options[pos++] = type;
1377 
1378   memcpy(&pack->dhcp.options[pos], conn->dhcp_opts.options, DHCP_OPTIONS_LEN-pos);
1379   pos += conn->dhcp_opts.option_length;
1380 
1381   return pos;
1382 }
1383 
1384 
1385 /**
1386  * dhcp_gettag()
1387  * Search a DHCP packet for a particular tag.
1388  * Returns -1 if not found.
1389  **/
dhcp_gettag(struct dhcp_packet_t * pack,size_t length,struct dhcp_tag_t ** tag,uint8_t tagtype)1390 int dhcp_gettag(struct dhcp_packet_t *pack, size_t length,
1391 		struct dhcp_tag_t **tag, uint8_t tagtype) {
1392   struct dhcp_tag_t *t;
1393   size_t offset = DHCP_MIN_LEN + DHCP_OPTION_MAGIC_LEN;
1394 
1395   /* if (length > DHCP_LEN) {
1396     log_warn(0,"Length of dhcp packet larger then %d: %d", DHCP_LEN, length);
1397     length = DHCP_LEN;
1398   } */
1399 
1400   while ((offset + 2) < length) {
1401     t = (struct dhcp_tag_t *)(((void *)pack) + offset);
1402     if (t->t == tagtype) {
1403       if ((offset +  2 + (size_t)(t->l)) > length)
1404 	return -1; /* Tag length too long */
1405       *tag = t;
1406       return 0;
1407     }
1408     offset +=  2 + t->l;
1409   }
1410 
1411   return -1; /* Not found  */
1412 }
1413 
1414 
1415 /**
1416  * dhcp_sendOFFER()
1417  * Send of a DHCP offer message to a peer.
1418  **/
dhcp_sendOFFER(struct dhcp_conn_t * conn,struct dhcp_fullpacket_t * pack,size_t len)1419 int dhcp_sendOFFER(struct dhcp_conn_t *conn,
1420 		   struct dhcp_fullpacket_t *pack, size_t len) {
1421 
1422   struct dhcp_t *this = conn->parent;
1423   struct dhcp_fullpacket_t packet;
1424   uint16_t length = 576 + 4; /* Maximum length */
1425   uint16_t udp_len = 576 - 20; /* Maximum length */
1426   size_t pos = 0;
1427 
1428   /* Get packet default values */
1429   pos = dhcp_create_pkt(DHCPOFFER, &packet, pack, conn);
1430 
1431   /* DHCP Payload */
1432 
1433   packet.dhcp.options[pos++] = DHCP_OPTION_SUBNET_MASK;
1434   packet.dhcp.options[pos++] = 4;
1435   memcpy(&packet.dhcp.options[pos], &conn->hismask.s_addr, 4);
1436   pos += 4;
1437 
1438   packet.dhcp.options[pos++] = DHCP_OPTION_ROUTER_OPTION;
1439   packet.dhcp.options[pos++] = 4;
1440   memcpy(&packet.dhcp.options[pos], &conn->ourip.s_addr, 4);
1441   pos += 4;
1442 
1443   /* Insert DNS Servers if given */
1444   if (conn->dns1.s_addr && conn->dns2.s_addr) {
1445     packet.dhcp.options[pos++] = DHCP_OPTION_DNS;
1446     packet.dhcp.options[pos++] = 8;
1447     memcpy(&packet.dhcp.options[pos], &conn->dns1.s_addr, 4);
1448     pos += 4;
1449     memcpy(&packet.dhcp.options[pos], &conn->dns2.s_addr, 4);
1450     pos += 4;
1451   }
1452   else if (conn->dns1.s_addr) {
1453     packet.dhcp.options[pos++] = DHCP_OPTION_DNS;
1454     packet.dhcp.options[pos++] = 4;
1455     memcpy(&packet.dhcp.options[pos], &conn->dns1.s_addr, 4);
1456     pos += 4;
1457   }
1458   else if (conn->dns2.s_addr) {
1459     packet.dhcp.options[pos++] = DHCP_OPTION_DNS;
1460     packet.dhcp.options[pos++] = 4;
1461     memcpy(&packet.dhcp.options[pos], &conn->dns2.s_addr, 4);
1462     pos += 4;
1463   }
1464 
1465   /* Insert Domain Name if present */
1466   if (strlen(conn->domain)) {
1467     packet.dhcp.options[pos++] = DHCP_OPTION_DOMAIN_NAME;
1468     packet.dhcp.options[pos++] = strlen(conn->domain);
1469     memcpy(&packet.dhcp.options[pos], &conn->domain, strlen(conn->domain));
1470     pos += strlen(conn->domain);
1471   }
1472 
1473   packet.dhcp.options[pos++] = DHCP_OPTION_LEASE_TIME;
1474   packet.dhcp.options[pos++] = 4;
1475   packet.dhcp.options[pos++] = (this->lease >> 24) & 0xFF;
1476   packet.dhcp.options[pos++] = (this->lease >> 16) & 0xFF;
1477   packet.dhcp.options[pos++] = (this->lease >>  8) & 0xFF;
1478   packet.dhcp.options[pos++] = (this->lease >>  0) & 0xFF;
1479 
1480   /* Must be listening address */
1481   packet.dhcp.options[pos++] = DHCP_OPTION_SERVER_ID;
1482   packet.dhcp.options[pos++] = 4;
1483   memcpy(&packet.dhcp.options[pos], &conn->ourip.s_addr, 4);
1484   pos += 4;
1485 
1486   packet.dhcp.options[pos++] = DHCP_OPTION_END;
1487 
1488   /* UDP header */
1489   udp_len = pos + DHCP_MIN_LEN + PKT_UDP_HLEN;
1490   packet.udph.len = htons(udp_len);
1491 
1492   /* IP header */
1493   packet.iph.tot_len = htons(udp_len + PKT_IP_HLEN);
1494 
1495   /* Work out checksums */
1496   chksum(&packet.iph);
1497 
1498   /* Calculate total length */
1499   length = udp_len + PKT_IP_HLEN + PKT_ETH_HLEN;
1500 
1501   return dhcp_send(this, &this->ipif, conn->hismac, &packet, length);
1502 }
1503 
1504 /**
1505  * dhcp_sendACK()
1506  * Send of a DHCP acknowledge message to a peer.
1507  **/
dhcp_sendACK(struct dhcp_conn_t * conn,struct dhcp_fullpacket_t * pack,size_t len)1508 int dhcp_sendACK(struct dhcp_conn_t *conn,
1509 		 struct dhcp_fullpacket_t *pack, size_t len) {
1510 
1511   struct dhcp_t *this = conn->parent;
1512   struct dhcp_fullpacket_t packet;
1513   uint16_t length = 576 + 4; /* Maximum length */
1514   uint16_t udp_len = 576 - 20; /* Maximum length */
1515   size_t pos = 0;
1516 
1517   /* Get packet default values */
1518   pos = dhcp_create_pkt(DHCPACK, &packet, pack, conn);
1519 
1520   /* DHCP Payload */
1521   packet.dhcp.options[pos++] = DHCP_OPTION_SUBNET_MASK;
1522   packet.dhcp.options[pos++] = 4;
1523   memcpy(&packet.dhcp.options[pos], &conn->hismask.s_addr, 4);
1524   pos += 4;
1525 
1526   packet.dhcp.options[pos++] = DHCP_OPTION_ROUTER_OPTION;
1527   packet.dhcp.options[pos++] = 4;
1528   memcpy(&packet.dhcp.options[pos], &conn->ourip.s_addr, 4);
1529   pos += 4;
1530 
1531   /* Insert DNS Servers if given */
1532   if (conn->dns1.s_addr && conn->dns2.s_addr) {
1533     packet.dhcp.options[pos++] = DHCP_OPTION_DNS;
1534     packet.dhcp.options[pos++] = 8;
1535     memcpy(&packet.dhcp.options[pos], &conn->dns1.s_addr, 4);
1536     pos += 4;
1537     memcpy(&packet.dhcp.options[pos], &conn->dns2.s_addr, 4);
1538     pos += 4;
1539   }
1540   else if (conn->dns1.s_addr) {
1541     packet.dhcp.options[pos++] = DHCP_OPTION_DNS;
1542     packet.dhcp.options[pos++] = 4;
1543     memcpy(&packet.dhcp.options[pos], &conn->dns1.s_addr, 4);
1544     pos += 4;
1545   }
1546   else if (conn->dns2.s_addr) {
1547     packet.dhcp.options[pos++] = DHCP_OPTION_DNS;
1548     packet.dhcp.options[pos++] = 4;
1549     memcpy(&packet.dhcp.options[pos], &conn->dns2.s_addr, 4);
1550     pos += 4;
1551   }
1552 
1553   /* Insert Domain Name if present */
1554   if (strlen(conn->domain)) {
1555     packet.dhcp.options[pos++] = DHCP_OPTION_DOMAIN_NAME;
1556     packet.dhcp.options[pos++] = strlen(conn->domain);
1557     memcpy(&packet.dhcp.options[pos], &conn->domain, strlen(conn->domain));
1558     pos += strlen(conn->domain);
1559   }
1560 
1561   packet.dhcp.options[pos++] = DHCP_OPTION_LEASE_TIME;
1562   packet.dhcp.options[pos++] = 4;
1563   packet.dhcp.options[pos++] = (this->lease >> 24) & 0xFF;
1564   packet.dhcp.options[pos++] = (this->lease >> 16) & 0xFF;
1565   packet.dhcp.options[pos++] = (this->lease >>  8) & 0xFF;
1566   packet.dhcp.options[pos++] = (this->lease >>  0) & 0xFF;
1567 
1568   /*
1569   packet.dhcp.options[pos++] = DHCP_OPTION_INTERFACE_MTU;
1570   packet.dhcp.options[pos++] = 2;
1571   packet.dhcp.options[pos++] = (conn->mtu >> 8) & 0xFF;
1572   packet.dhcp.options[pos++] = (conn->mtu >> 0) & 0xFF;
1573   */
1574 
1575   /* Must be listening address */
1576   packet.dhcp.options[pos++] = DHCP_OPTION_SERVER_ID;
1577   packet.dhcp.options[pos++] = 4;
1578   memcpy(&packet.dhcp.options[pos], &conn->ourip.s_addr, 4);
1579   pos += 4;
1580 
1581   packet.dhcp.options[pos++] = DHCP_OPTION_END;
1582 
1583   /* UDP header */
1584   udp_len = pos + DHCP_MIN_LEN + PKT_UDP_HLEN;
1585   packet.udph.len = htons(udp_len);
1586 
1587   /* IP header */
1588   packet.iph.tot_len = htons(udp_len + PKT_IP_HLEN);
1589 
1590   /* Work out checksums */
1591   chksum(&packet.iph);
1592 
1593   /* Calculate total length */
1594   length = udp_len + PKT_IP_HLEN + PKT_ETH_HLEN;
1595 
1596   return dhcp_send(this, &this->ipif, conn->hismac, &packet, length);
1597 }
1598 
1599 /**
1600  * dhcp_sendNAK()
1601  * Send of a DHCP negative acknowledge message to a peer.
1602  * NAK messages are always sent to broadcast IP address (
1603  * except when using a DHCP relay server)
1604  **/
dhcp_sendNAK(struct dhcp_conn_t * conn,struct dhcp_fullpacket_t * pack,size_t len)1605 int dhcp_sendNAK(struct dhcp_conn_t *conn,
1606 		 struct dhcp_fullpacket_t *pack, size_t len) {
1607 
1608   struct dhcp_t *this = conn->parent;
1609   struct dhcp_fullpacket_t packet;
1610   uint16_t length = 576 + 4; /* Maximum length */
1611   uint16_t udp_len = 576 - 20; /* Maximum length */
1612   size_t pos = 0;
1613 
1614   /* Get packet default values */
1615   pos = dhcp_create_pkt(DHCPNAK, &packet, pack, conn);
1616 
1617   /* DHCP Payload */
1618 
1619   /* Must be listening address */
1620   packet.dhcp.options[pos++] = DHCP_OPTION_SERVER_ID;
1621   packet.dhcp.options[pos++] = 4;
1622   memcpy(&packet.dhcp.options[pos], &conn->ourip.s_addr, 4);
1623   pos += 4;
1624 
1625   packet.dhcp.options[pos++] = DHCP_OPTION_END;
1626 
1627   /* UDP header */
1628   udp_len = pos + DHCP_MIN_LEN + PKT_UDP_HLEN;
1629   packet.udph.len = htons(udp_len);
1630 
1631   /* IP header */
1632   packet.iph.tot_len = htons(udp_len + PKT_IP_HLEN);
1633 
1634   /* Work out checksums */
1635   chksum(&packet.iph);
1636 
1637   /* Calculate total length */
1638   length = udp_len + PKT_IP_HLEN + PKT_ETH_HLEN;
1639 
1640   return dhcp_send(this, &this->ipif, conn->hismac, &packet, length);
1641 }
1642 
1643 
1644 /**
1645  *  dhcp_getreq()
1646  *  Process a received DHCP request and sends a response.
1647  **/
dhcp_getreq(struct dhcp_t * this,struct dhcp_fullpacket_t * pack,size_t len)1648 int dhcp_getreq(struct dhcp_t *this, struct dhcp_fullpacket_t *pack, size_t len) {
1649   uint8_t mac[PKT_ETH_ALEN];
1650   struct dhcp_tag_t *message_type = 0;
1651   struct dhcp_tag_t *requested_ip = 0;
1652   struct dhcp_conn_t *conn;
1653   struct in_addr addr;
1654 
1655   if (pack->udph.dst != htons(DHCP_BOOTPS))
1656     return 0; /* Not a DHCP packet */
1657 
1658   if (dhcp_gettag(&pack->dhcp, ntohs(pack->udph.len)-PKT_UDP_HLEN,
1659 		  &message_type, DHCP_OPTION_MESSAGE_TYPE)) {
1660     return -1;
1661   }
1662 
1663   if (message_type->l != 1)
1664     return -1; /* Wrong length of message type */
1665 
1666   if (pack->dhcp.giaddr)
1667     memcpy(mac, pack->dhcp.chaddr, PKT_ETH_ALEN);
1668   else
1669     memcpy(mac, pack->ethh.src, PKT_ETH_ALEN);
1670 
1671   switch(message_type->v[0]) {
1672 
1673   case DHCPRELEASE:
1674     dhcp_release_mac(this, mac, RADIUS_TERMINATE_CAUSE_LOST_CARRIER);
1675 
1676   case DHCPDISCOVER:
1677   case DHCPREQUEST:
1678   case DHCPINFORM:
1679     break;
1680 
1681   default:
1682     return 0; /* Unsupported message type */
1683   }
1684 
1685   if (this->relayfd > 0) {
1686     /** Relay the DHCP request **/
1687     struct sockaddr_in addr;
1688 
1689     memset(&addr, 0, sizeof(addr));
1690     addr.sin_family = AF_INET;
1691     addr.sin_addr.s_addr = options.dhcpgwip.s_addr;
1692     addr.sin_port = htons(options.dhcpgwport);
1693 
1694     if (options.dhcprelayip.s_addr)
1695       pack->dhcp.giaddr = options.dhcprelayip.s_addr;
1696     else
1697       pack->dhcp.giaddr = options.uamlisten.s_addr;
1698 
1699     /* if we can't send, lets do dhcp ourselves */
1700     if (sendto(this->relayfd, &pack->dhcp, ntohs(pack->udph.len) - PKT_UDP_HLEN, 0,
1701 	       (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1702       log_err(errno, "could not relay DHCP request!");
1703     }
1704     else {
1705       return 0;
1706     }
1707   }
1708 
1709   if (message_type->v[0] == DHCPRELEASE) {
1710     /* No Reply to client is sent */
1711     return 0;
1712   }
1713 
1714   /* Check to see if we know MAC address. If not allocate new conn */
1715   if (dhcp_hashget(this, &conn, mac)) {
1716 
1717     /* Do we allow dynamic allocation of IP addresses? */
1718     if (!this->allowdyn) /* TODO: Should be deleted! */
1719       return 0;
1720 
1721     /* Allocate new connection */
1722     if (dhcp_newconn(this, &conn, mac)) /* TODO: Delete! */
1723       return 0; /* Out of connections */
1724   }
1725 
1726   /* Request an IP address */
1727   if (conn->authstate == DHCP_AUTH_NONE) {
1728     addr.s_addr = pack->dhcp.ciaddr;
1729     if (this->cb_request)
1730       if (this->cb_request(conn, &addr, pack, len)) {
1731 	return 0; /* Ignore request if IP address was not allocated */
1732       }
1733   }
1734 
1735   conn->lasttime = mainclock;
1736 
1737   /* Discover message */
1738   /* If an IP address was assigned offer it to the client */
1739   /* Otherwise ignore the request */
1740   if (message_type->v[0] == DHCPDISCOVER) {
1741     if (conn->hisip.s_addr)
1742       dhcp_sendOFFER(conn, pack, len);
1743     return 0;
1744   }
1745 
1746   /* Request message */
1747   if (message_type->v[0] == DHCPREQUEST) {
1748 
1749     if (!conn->hisip.s_addr) {
1750       if (this->debug) log_dbg("hisip not set");
1751       return dhcp_sendNAK(conn, pack, len);
1752     }
1753 
1754     if (!memcmp(&conn->hisip.s_addr, &pack->dhcp.ciaddr, 4)) {
1755       if (this->debug) log_dbg("hisip match ciaddr");
1756       return dhcp_sendACK(conn, pack, len);
1757     }
1758 
1759     if (!dhcp_gettag(&pack->dhcp, ntohs(pack->udph.len)-PKT_UDP_HLEN,
1760 		     &requested_ip, DHCP_OPTION_REQUESTED_IP)) {
1761       if (!memcmp(&conn->hisip.s_addr, requested_ip->v, 4))
1762 	return dhcp_sendACK(conn, pack, len);
1763     }
1764 
1765     if (this->debug) log_dbg("Sending NAK to client");
1766     return dhcp_sendNAK(conn, pack, len);
1767   }
1768 
1769   /*
1770    *  Unsupported DHCP message: Ignore
1771    */
1772   if (this->debug) log_dbg("Unsupported DHCP message ignored");
1773   return 0;
1774 }
1775 
1776 
1777 /**
1778  * dhcp_set_addrs()
1779  * Set various IP addresses of a connection.
1780  **/
dhcp_set_addrs(struct dhcp_conn_t * conn,struct in_addr * hisip,struct in_addr * hismask,struct in_addr * ourip,struct in_addr * ourmask,struct in_addr * dns1,struct in_addr * dns2,char * domain)1781 int dhcp_set_addrs(struct dhcp_conn_t *conn, struct in_addr *hisip,
1782 		   struct in_addr *hismask, struct in_addr *ourip,
1783 		   struct in_addr *ourmask, struct in_addr *dns1,
1784 		   struct in_addr *dns2, char *domain) {
1785 
1786   conn->hisip.s_addr = hisip->s_addr;
1787   conn->hismask.s_addr = hismask->s_addr;
1788   conn->ourip.s_addr = ourip->s_addr;
1789   conn->dns1.s_addr = dns1->s_addr;
1790   conn->dns2.s_addr = dns2->s_addr;
1791 
1792   if (domain) {
1793     strncpy(conn->domain, domain, DHCP_DOMAIN_LEN);
1794     conn->domain[DHCP_DOMAIN_LEN-1] = 0;
1795   }
1796   else {
1797     conn->domain[0] = 0;
1798   }
1799 
1800   if (options.uamanyip &&
1801       (hisip->s_addr & ourmask->s_addr) != (ourip->s_addr & ourmask->s_addr)) {
1802     /**
1803      *  We have enabled ''uamanyip'' and the address we are setting does
1804      *  not fit in ourip's network. In this case, add a route entry.
1805      */
1806     struct app_conn_t *appconn = (struct app_conn_t *)conn->peer;
1807     if (appconn) {
1808       struct ippoolm_t *ipm = (struct ippoolm_t*)appconn->uplink;
1809       if (ipm && ipm->inuse == 2) {
1810 	struct in_addr mask;
1811 	mask.s_addr = 0xffffffff;
1812 	log_dbg("Adding route for %s %d", inet_ntoa(*hisip),
1813 		net_add_route(hisip, ourip, &mask));
1814       }
1815     }
1816   }
1817 
1818   return 0;
1819 }
1820 
1821 static unsigned char const bmac[PKT_ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
1822 
dhcp_receive_ip(struct dhcp_t * this,struct pkt_ippacket_t * pack,size_t len)1823 int dhcp_receive_ip(struct dhcp_t *this, struct pkt_ippacket_t *pack, size_t len) {
1824   struct pkt_tcphdr_t *tcph = (struct pkt_tcphdr_t*) pack->payload;
1825   /*struct pkt_udphdr_t *udph = (struct pkt_udphdr_t*) pack->payload;*/
1826   struct dhcp_conn_t *conn;
1827   struct in_addr ourip;
1828   struct in_addr addr;
1829 
1830   /*
1831    *  Received a packet from the dhcpif
1832    */
1833 
1834   if (this->debug)
1835     log_dbg("DHCP packet received");
1836 
1837   /*
1838    *  Check that the destination MAC address is our MAC or Broadcast
1839    */
1840   if ((memcmp(pack->ethh.dst, this->ipif.hwaddr, PKT_ETH_ALEN)) &&
1841       (memcmp(pack->ethh.dst, bmac, PKT_ETH_ALEN))) {
1842     log_dbg("dropping packet; not for our MAC or broadcast");
1843     return 0;
1844   }
1845 
1846   ourip.s_addr = this->ourip.s_addr;
1847 
1848   /*
1849    *  DHCP (BOOTPS) packets for broadcast or us specifically
1850    */
1851   if (((pack->iph.daddr == 0) ||
1852        (pack->iph.daddr == 0xffffffff) ||
1853        (pack->iph.daddr == ourip.s_addr)) &&
1854       ((pack->iph.version_ihl == PKT_IP_VER_HLEN) &&
1855        (pack->iph.protocol == PKT_IP_PROTO_UDP) &&
1856        (((struct dhcp_fullpacket_t*)pack)->udph.dst == htons(DHCP_BOOTPS)))) {
1857     log_dbg("dhcp/bootps request being processed");
1858     return dhcp_getreq(this, (struct dhcp_fullpacket_t*) pack, len);
1859   }
1860 
1861   /*
1862    *  Check to see if we know MAC address
1863    */
1864   if (!dhcp_hashget(this, &conn, pack->ethh.src)) {
1865     if (this->debug) log_dbg("Address found");
1866     ourip.s_addr = conn->ourip.s_addr;
1867   }
1868   else {
1869     /* ALPAPAD */
1870     struct in_addr reqaddr;
1871     /* Get local copy */
1872     memcpy(&reqaddr.s_addr, &pack->iph.saddr, PKT_IP_ALEN);
1873 
1874     if (options.debug)
1875       log_dbg("Address not found (%s)", inet_ntoa(reqaddr));
1876 
1877     /* Do we allow dynamic allocation of IP addresses? */
1878     if (!this->allowdyn && !options.uamanyip)
1879       return 0;
1880 
1881     /* Allocate new connection */
1882     if (dhcp_newconn(this, &conn, pack->ethh.src)) {
1883       if (this->debug)
1884 	log_dbg("dropping packet; out of connections");
1885       return 0; /* Out of connections */
1886     }
1887   }
1888 
1889   /* Request an IP address
1890   if (options.uamanyip &&
1891       conn->authstate == DHCP_AUTH_NONE) {
1892     this->cb_request(conn, &pack->iph.saddr);
1893   } */
1894 
1895   /* Return if we do not know peer */
1896   if (!conn) {
1897     if (this->debug)
1898       log_dbg("dropping packet; no peer");
1899     return 0;
1900   }
1901 
1902   /*
1903    *  Request an IP address
1904    */
1905   if ((conn->authstate == DHCP_AUTH_NONE) &&
1906       (options.uamanyip ||
1907        ((pack->iph.daddr != 0) &&
1908 	(pack->iph.daddr != 0xffffffff)))) {
1909     addr.s_addr = pack->iph.saddr;
1910     if (this->cb_request)
1911       if (this->cb_request(conn, &addr, 0, 0)) {
1912 	if (this->debug)
1913 	  log_dbg("dropping packet; ip not known");
1914 	return 0; /* Ignore request if IP address was not allocated */
1915       }
1916   }
1917 
1918 
1919   conn->lasttime = mainclock;
1920 
1921   /*
1922   if (((pack->iph.daddr == conn->dns1.s_addr) ||
1923        (pack->iph.daddr == conn->dns2.s_addr)) &&
1924       (pack->iph.protocol == PKT_IP_PROTO_UDP) &&
1925       (udph->dst == htons(DHCP_DNS))) {
1926     if (dhcp_checkDNS(conn, pack, len)) return 0;
1927     }*/
1928 
1929   /* Was it a request for the auto-logout service? */
1930   if ((pack->iph.daddr == options.uamlogout.s_addr) &&
1931       (pack->iph.protocol == PKT_IP_PROTO_TCP) &&
1932       (tcph->dst == htons(DHCP_HTTP))) {
1933     if (conn->peer) {
1934       struct app_conn_t *appconn = (struct app_conn_t *)conn->peer;
1935       if (appconn->s_state.authenticated) {
1936 	terminate_appconn(appconn, RADIUS_TERMINATE_CAUSE_USER_REQUEST);
1937 	if (options.debug)
1938 	  log_dbg("Dropping session due to request for auto-logout ip");
1939 	appconn->uamexit=1;
1940       }
1941     }
1942   }
1943 
1944   switch (conn->authstate) {
1945   case DHCP_AUTH_PASS:
1946     /* Check for post-auth proxy, otherwise pass packets unmodified */
1947     dhcp_postauthDNAT(conn, pack, len, 0);
1948     break;
1949 
1950   case DHCP_AUTH_UNAUTH_TOS:
1951     /* Set TOS to specified value (unauthenticated) */
1952     pack->iph.tos = conn->unauth_cp;
1953     chksum(&pack->iph);
1954     break;
1955 
1956   case DHCP_AUTH_AUTH_TOS:
1957     /* Set TOS to specified value (authenticated) */
1958     pack->iph.tos = conn->auth_cp;
1959     chksum(&pack->iph);
1960     break;
1961 
1962   case DHCP_AUTH_SPLASH:
1963     dhcp_doDNAT(conn, pack, len);
1964     break;
1965 
1966   case DHCP_AUTH_DNAT:
1967     /* Destination NAT if request to unknown web server */
1968     if (dhcp_doDNAT(conn, pack, len)) {
1969       if (this->debug) log_dbg("dropping packet; not nat'ed");
1970       return 0; /* Drop is not http or dns */
1971     }
1972     break;
1973 
1974   case DHCP_AUTH_DROP:
1975   default:
1976     if (this->debug)
1977       log_dbg("dropping packet; auth-drop");
1978     return 0;
1979   }
1980 
1981   /*done:*/
1982 
1983   if (options.usetap) {
1984     struct pkt_ethhdr_t *ethh = (struct pkt_ethhdr_t *)pack;
1985     memcpy(ethh->dst,tuntap(tun).hwaddr,PKT_ETH_ALEN);
1986   }
1987 
1988   if ((conn->hisip.s_addr) && (this->cb_data_ind)) {
1989     this->cb_data_ind(conn, pack, len);
1990   } else {
1991     if (this->debug)
1992       log_dbg("no hisip; packet-drop");
1993   }
1994 
1995   return 0;
1996 }
1997 
1998 /**
1999  * Call this function when a new IP packet has arrived. This function
2000  * should be part of a select() loop in the application.
2001  **/
dhcp_decaps(struct dhcp_t * this)2002 int dhcp_decaps(struct dhcp_t *this) {
2003   struct pkt_ippacket_t packet;
2004   ssize_t length;
2005 
2006   if ((length = net_read(&this->ipif, &packet, sizeof(packet))) < 0)
2007     return -1;
2008 
2009   if (this->debug) {
2010     struct pkt_ethhdr_t *ethh = &packet.ethh;
2011     log_dbg("dhcp_decaps: dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x prot=%.4x",
2012 	    ethh->dst[0],ethh->dst[1],ethh->dst[2],ethh->dst[3],ethh->dst[4],ethh->dst[5],
2013 	    ethh->src[0],ethh->src[1],ethh->src[2],ethh->src[3],ethh->src[4],ethh->src[5],
2014 	    ntohs(ethh->prot));
2015   }
2016 
2017   return dhcp_receive_ip(this, &packet, length);
2018 }
2019 
dhcp_relay_decaps(struct dhcp_t * this)2020 int dhcp_relay_decaps(struct dhcp_t *this) {
2021   struct dhcp_tag_t *message_type = 0;
2022   struct dhcp_fullpacket_t fullpack;
2023   struct dhcp_conn_t *conn;
2024   struct dhcp_packet_t packet;
2025   struct sockaddr_in addr;
2026   socklen_t fromlen = sizeof(addr);
2027   ssize_t length;
2028 
2029 
2030   if ((length = recvfrom(this->relayfd, &packet, sizeof(packet), 0,
2031                          (struct sockaddr *) &addr, &fromlen)) <= 0) {
2032     log_err(errno, "recvfrom() failed");
2033     return -1;
2034   }
2035 
2036   log_dbg("DHCP relay response of length %d received", length);
2037 
2038   if (addr.sin_addr.s_addr != options.dhcpgwip.s_addr) {
2039     log_err(0, "received DHCP response from host other than our gateway");
2040     return -1;
2041   }
2042 
2043   if (addr.sin_port != htons(options.dhcpgwport)) {
2044     log_err(0, "received DHCP response from port other than our gateway");
2045     return -1;
2046   }
2047 
2048   if (dhcp_gettag(&packet, length, &message_type, DHCP_OPTION_MESSAGE_TYPE)) {
2049     log_err(0, "no message type");
2050     return -1;
2051   }
2052 
2053   if (message_type->l != 1) {
2054     log_err(0, "wrong message type length");
2055     return -1; /* Wrong length of message type */
2056   }
2057 
2058   if (dhcp_hashget(this, &conn, packet.chaddr)) {
2059 
2060     /* Allocate new connection */
2061     if (dhcp_newconn(this, &conn, packet.chaddr)) {
2062       log_err(0, "out of connections");
2063       return 0; /* Out of connections */
2064     }
2065 
2066     this->cb_request(conn, (struct in_addr *)&packet.yiaddr, 0, 0);
2067   }
2068 
2069   packet.giaddr = 0;
2070 
2071   memset(&fullpack, 0, sizeof(fullpack));
2072 
2073   memcpy(fullpack.ethh.dst, conn->hismac, PKT_ETH_ALEN);
2074   memcpy(fullpack.ethh.src, this->ipif.hwaddr, PKT_ETH_ALEN);
2075   fullpack.ethh.prot = htons(PKT_ETH_PROTO_IP);
2076 
2077   fullpack.iph.version_ihl = PKT_IP_VER_HLEN;
2078   fullpack.iph.tot_len = htons(length + PKT_UDP_HLEN + PKT_IP_HLEN);
2079   fullpack.iph.ttl = 0x10;
2080   fullpack.iph.protocol = 0x11;
2081 
2082   fullpack.iph.saddr = conn->ourip.s_addr;
2083   fullpack.udph.src = htons(DHCP_BOOTPS);
2084   fullpack.udph.len = htons(length + PKT_UDP_HLEN);
2085 
2086   /*if (fullpack.dhcp.ciaddr) {
2087     fullpack.udph.daddr = req->dhcp.ciaddr;
2088     fullpack.udph.dst = htons(DHCP_BOOTPC);
2089   } else if (req->dhcp.giaddr) {
2090     fullpack.iph.daddr = req->dhcp.giaddr;
2091     fullpack.udph.dst = htons(DHCP_BOOTPS);
2092     } else */
2093 
2094   if (message_type->v[0] == DHCPNAK || packet.flags[0] & 0x80) {
2095     fullpack.iph.daddr = ~0;
2096     fullpack.udph.dst = htons(DHCP_BOOTPC);
2097     fullpack.dhcp.flags[0] = 0x80;
2098   } if (packet.ciaddr) {
2099     fullpack.iph.daddr = packet.ciaddr;
2100     fullpack.udph.dst = htons(DHCP_BOOTPC);
2101   } else {
2102     fullpack.iph.daddr = packet.yiaddr;
2103     fullpack.udph.dst = htons(DHCP_BOOTPC);
2104   }
2105 
2106   memcpy(&fullpack.dhcp, &packet, sizeof(packet));
2107 
2108   { /* rewrite the server-id, otherwise will not get subsequent requests */
2109     struct dhcp_tag_t *tag = 0;
2110     if (!dhcp_gettag(&fullpack.dhcp, length, &tag, DHCP_OPTION_SERVER_ID)) {
2111       memcpy(tag->v, &conn->ourip.s_addr, 4);
2112     }
2113   }
2114 
2115   chksum(&fullpack.iph);
2116 
2117   return dhcp_send(this, &this->ipif, conn->hismac, &fullpack,
2118 		   length + PKT_UDP_HLEN + PKT_IP_HLEN + PKT_ETH_HLEN);
2119 }
2120 
2121 /**
2122  * dhcp_data_req()
2123  * Call this function to send an IP packet to the peer.
2124  * Called from the tun_ind function. This method is passed either
2125  * an Ethernet frame or an IP packet.
2126  **/
dhcp_data_req(struct dhcp_conn_t * conn,void * pack,size_t len,int ethhdr)2127 int dhcp_data_req(struct dhcp_conn_t *conn, void *pack, size_t len, int ethhdr) {
2128   struct dhcp_t *this = conn->parent;
2129   struct pkt_ippacket_t packet;
2130   size_t length = len;
2131 
2132   if (ethhdr) { /* Ethernet frame */
2133     memcpy(&packet, pack, len);
2134   } else {      /* IP packet */
2135     memcpy(&packet.iph, pack, len);
2136     length += PKT_ETH_HLEN;
2137   }
2138 
2139   /* Ethernet header */
2140   memcpy(packet.ethh.dst, conn->hismac, PKT_ETH_ALEN);
2141   memcpy(packet.ethh.src, this->ipif.hwaddr, PKT_ETH_ALEN);
2142   packet.ethh.prot = htons(PKT_ETH_PROTO_IP);
2143 
2144   switch (conn->authstate) {
2145 
2146   case DHCP_AUTH_PASS:
2147   case DHCP_AUTH_AUTH_TOS:
2148     dhcp_postauthDNAT(conn, &packet, length, 1);
2149     break;
2150 
2151   case DHCP_AUTH_SPLASH:
2152   case DHCP_AUTH_UNAUTH_TOS:
2153     dhcp_undoDNAT(conn, &packet, &length);
2154     break;
2155 
2156   case DHCP_AUTH_DNAT:
2157     /* undo destination NAT */
2158     if (dhcp_undoDNAT(conn, &packet, &length)) {
2159       if (this->debug) log_dbg("dhcp_undoDNAT() returns true");
2160       return 0;
2161     }
2162     break;
2163 
2164   case DHCP_AUTH_DROP:
2165   default: return 0;
2166   }
2167 
2168   return dhcp_send(this, &this->ipif, conn->hismac, &packet, length);
2169 }
2170 
2171 
2172 /**
2173  * dhcp_sendARP()
2174  * Send ARP message to peer
2175  **/
2176 static int
dhcp_sendARP(struct dhcp_conn_t * conn,struct arp_fullpacket_t * pack,size_t len)2177 dhcp_sendARP(struct dhcp_conn_t *conn, struct arp_fullpacket_t *pack, size_t len) {
2178 
2179   struct dhcp_t *this = conn->parent;
2180   struct arp_fullpacket_t packet;
2181   struct in_addr reqaddr;
2182   size_t length = sizeof(packet);
2183 
2184   /* Get local copy */
2185   memcpy(&reqaddr.s_addr, pack->arp.tpa, PKT_IP_ALEN);
2186 
2187   /* Check that request is within limits */
2188 
2189   /* Get packet default values */
2190   memset(&packet, 0, sizeof(packet));
2191 
2192   /* ARP Payload */
2193   packet.arp.hrd = htons(DHCP_HTYPE_ETH);
2194   packet.arp.pro = htons(PKT_ETH_PROTO_IP);
2195   packet.arp.hln = PKT_ETH_ALEN;
2196   packet.arp.pln = PKT_IP_ALEN;
2197   packet.arp.op  = htons(DHCP_ARP_REPLY);
2198 
2199   /* Source address */
2200   memcpy(packet.arp.sha, this->arpif.hwaddr, PKT_ETH_ALEN);
2201   memcpy(packet.arp.spa, &reqaddr.s_addr, PKT_IP_ALEN);
2202 
2203   /* Target address */
2204   memcpy(packet.arp.tha, &conn->hismac, PKT_ETH_ALEN);
2205   memcpy(packet.arp.tpa, &conn->hisip.s_addr, PKT_IP_ALEN);
2206 
2207   /* Ethernet header */
2208   memcpy(packet.ethh.dst, conn->hismac, PKT_ETH_ALEN);
2209   memcpy(packet.ethh.src, this->ipif.hwaddr, PKT_ETH_ALEN);
2210   packet.ethh.prot = htons(PKT_ETH_PROTO_ARP);
2211 
2212   return dhcp_send(this, &this->arpif, conn->hismac, &packet, length);
2213 }
2214 
2215 
dhcp_receive_arp(struct dhcp_t * this,struct arp_fullpacket_t * pack,size_t len)2216 int dhcp_receive_arp(struct dhcp_t *this,
2217 		     struct arp_fullpacket_t *pack, size_t len) {
2218 
2219   unsigned char const bmac[PKT_ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
2220   struct dhcp_conn_t *conn;
2221   struct in_addr reqaddr;
2222   struct in_addr taraddr;
2223 
2224   /* Check that this is ARP request */
2225   if (pack->arp.op != htons(DHCP_ARP_REQUEST)) {
2226     if (this->debug)
2227       log_dbg("Received other ARP than request!");
2228     return 0;
2229   }
2230 
2231   /* Check that MAC address is our MAC or Broadcast */
2232   if ((memcmp(pack->ethh.dst, this->ipif.hwaddr, PKT_ETH_ALEN)) &&
2233       (memcmp(pack->ethh.dst, bmac, PKT_ETH_ALEN))) {
2234     if (this->debug)
2235       log_dbg("Received ARP request for other destination!");
2236     return 0;
2237   }
2238 
2239   /* get sender IP address */
2240   memcpy(&reqaddr.s_addr, &pack->arp.spa, PKT_IP_ALEN);
2241 
2242   /* get target IP address */
2243   memcpy(&taraddr.s_addr, &pack->arp.tpa, PKT_IP_ALEN);
2244 
2245 
2246   /* Check to see if we know MAC address. */
2247   if (dhcp_hashget(this, &conn, pack->ethh.src)) {
2248 
2249     if (options.debug)
2250       log_dbg("Address not found: %s", inet_ntoa(reqaddr));
2251 
2252     /* Do we allow dynamic allocation of IP addresses? */
2253     if (!this->allowdyn && !options.uamanyip) {
2254       if (this->debug)
2255 	log_dbg("ARP: Unknown client and no dynip: %s", inet_ntoa(taraddr));
2256       return 0;
2257     }
2258 
2259     /* Allocate new connection */
2260     if (dhcp_newconn(this, &conn, pack->ethh.src)) {
2261       log_warn(0, "ARP: out of connections");
2262       return 0; /* Out of connections */
2263     }
2264   }
2265 
2266   /* if no sender ip, then client is checking their own ip */
2267   if (!reqaddr.s_addr) {
2268     /* XXX: lookup in ippool to see if we really do know who has this */
2269     /* XXX: it should also ack if *we* are that ip */
2270     if (this->debug)
2271       log_dbg("ARP: Ignoring self-discovery: %s", inet_ntoa(taraddr));
2272 
2273     /* If a static ip address... */
2274     this->cb_request(conn, &taraddr, 0, 0);
2275 
2276     return 0;
2277   }
2278 
2279   if (!memcmp(&reqaddr.s_addr, &taraddr.s_addr, 4)) {
2280 
2281     /* Request an IP address */
2282     if (options.uamanyip /*or static ip*/ &&
2283 	conn->authstate == DHCP_AUTH_NONE) {
2284       this->cb_request(conn, &reqaddr, 0, 0);
2285     }
2286 
2287     if (this->debug)
2288       log_dbg("ARP: gratuitous arp %s!", inet_ntoa(taraddr));
2289 
2290     return 0;
2291   }
2292 
2293   if (!conn->hisip.s_addr && !options.uamanyip) {
2294     if (this->debug)
2295       log_dbg("ARP: request did not come from known client!");
2296     return 0; /* Only reply if he was allocated an address */
2297   }
2298 
2299   /* Is ARP request for clients own address: Ignore */
2300   if (conn->hisip.s_addr == taraddr.s_addr) {
2301     if (this->debug)
2302       log_dbg("ARP: hisip equals target ip: %s!",
2303 	      inet_ntoa(conn->hisip));
2304     return 0;
2305   }
2306 
2307   if (!options.uamanyip) {
2308     /* If ARP request outside of mask: Ignore */
2309     if (reqaddr.s_addr &&
2310 	(conn->hisip.s_addr & conn->hismask.s_addr) !=
2311 	(reqaddr.s_addr & conn->hismask.s_addr)) {
2312       if (this->debug)
2313 	log_dbg("ARP: request not in our subnet");
2314       return 0;
2315     }
2316 
2317     if (memcmp(&conn->ourip.s_addr, &taraddr.s_addr, 4)) { /* if ourip differs from target ip */
2318       if (options.debug) {
2319 	log_dbg("ARP: Did not ask for router address: %s", inet_ntoa(conn->ourip));
2320 	log_dbg("ARP: Asked for target: %s", inet_ntoa(taraddr));
2321       }
2322       return 0; /* Only reply if he asked for his router address */
2323     }
2324   }
2325   else if ((taraddr.s_addr != options.dhcplisten.s_addr) &&
2326           ((taraddr.s_addr & options.mask.s_addr) == options.net.s_addr)) {
2327     /* when uamanyip is on we should ignore arp requests that ARE within our subnet except of course the ones for ourselves */
2328     if (options.debug)
2329       log_dbg("ARP: request for IP=%s other than us within our subnet(uamanyip on), ignoring", inet_ntoa(taraddr));
2330     return 0;
2331   }
2332 
2333   conn->lasttime = mainclock;
2334 
2335   dhcp_sendARP(conn, pack, len);
2336 
2337   return 0;
2338 }
2339 
2340 
2341 /**
2342  * dhcp_arp_ind()
2343  * Call this function when a new ARP packet has arrived. This function
2344  * should be part of a select() loop in the application.
2345  **/
dhcp_arp_ind(struct dhcp_t * this)2346 int dhcp_arp_ind(struct dhcp_t *this)  /* ARP Indication */
2347 {
2348   struct arp_fullpacket_t packet;
2349   ssize_t length;
2350 
2351   if ((length = net_read(&this->arpif, &packet, sizeof(packet))) < 0)
2352     return -1;
2353 
2354   if (options.debug) {
2355     struct pkt_ethhdr_t *ethh = &packet.ethh;
2356     log_dbg("arp_decaps: dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x prot=%.4x",
2357 	    ethh->dst[0],ethh->dst[1],ethh->dst[2],ethh->dst[3],ethh->dst[4],ethh->dst[5],
2358 	    ethh->src[0],ethh->src[1],ethh->src[2],ethh->src[3],ethh->src[4],ethh->src[5],
2359 	    ntohs(ethh->prot));
2360   }
2361 
2362   dhcp_receive_arp(this, &packet, length);
2363 
2364   return 0;
2365 }
2366 
2367 
2368 /**
2369  * eapol_sendNAK()
2370  * Send of a EAPOL negative acknowledge message to a peer.
2371  * NAK messages are always sent to broadcast IP address (
2372  * except when using a EAPOL relay server)
2373  **/
dhcp_senddot1x(struct dhcp_conn_t * conn,struct dot1xpacket_t * pack,size_t len)2374 int dhcp_senddot1x(struct dhcp_conn_t *conn,
2375 		   struct dot1xpacket_t *pack, size_t len) {
2376   struct dhcp_t *this = conn->parent;
2377   return dhcp_send(this, &this->eapif, conn->hismac, pack, len);
2378 }
2379 
2380 /**
2381  * eapol_sendNAK()
2382  * Send of a EAPOL negative acknowledge message to a peer.
2383  * NAK messages are always sent to broadcast IP address (
2384  * except when using a EAPOL relay server)
2385  **/
dhcp_sendEAP(struct dhcp_conn_t * conn,void * pack,size_t len)2386 int dhcp_sendEAP(struct dhcp_conn_t *conn, void *pack, size_t len) {
2387 
2388   struct dhcp_t *this = conn->parent;
2389   struct dot1xpacket_t packet;
2390 
2391   /* Ethernet header */
2392   memcpy(packet.ethh.dst, conn->hismac, PKT_ETH_ALEN);
2393   memcpy(packet.ethh.src, this->ipif.hwaddr, PKT_ETH_ALEN);
2394   packet.ethh.prot = htons(PKT_ETH_PROTO_EAPOL);
2395 
2396   /* 802.1x header */
2397   packet.dot1x.ver  = 1;
2398   packet.dot1x.type = 0; /* EAP */
2399   packet.dot1x.len =  htons((uint16_t)len);
2400 
2401   memcpy(&packet.eap, pack, len);
2402 
2403   return dhcp_send(this, &this->eapif, conn->hismac, &packet, (PKT_ETH_HLEN + 4 + len));
2404 }
2405 
dhcp_sendEAPreject(struct dhcp_conn_t * conn,void * pack,size_t len)2406 int dhcp_sendEAPreject(struct dhcp_conn_t *conn, void *pack, size_t len) {
2407 
2408   /*struct dhcp_t *this = conn->parent;*/
2409 
2410   struct eap_packet_t packet;
2411 
2412   if (pack) {
2413     dhcp_sendEAP(conn, pack, len);
2414   }
2415   else {
2416     memset(&packet, 0, sizeof(packet));
2417     packet.code      =  4;
2418     packet.id        =  1; /* TODO ??? */
2419     packet.length    =  htons(4);
2420 
2421     dhcp_sendEAP(conn, &packet, 4);
2422   }
2423 
2424   return 0;
2425 
2426 }
2427 
dhcp_receive_eapol(struct dhcp_t * this,struct dot1xpacket_t * pack)2428 int dhcp_receive_eapol(struct dhcp_t *this, struct dot1xpacket_t *pack) {
2429   struct dhcp_conn_t *conn = NULL;
2430   unsigned char const bmac[PKT_ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
2431   unsigned char const amac[PKT_ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
2432 
2433   /* Check to see if we know MAC address. */
2434   if (!dhcp_hashget(this, &conn, pack->ethh.src)) {
2435     if (this->debug) log_dbg("Address found");
2436   }
2437   else {
2438     if (this->debug) log_dbg("Address not found");
2439   }
2440 
2441   if (this->debug)
2442     log_dbg("IEEE 802.1x Packet: %.2x, %.2x %d",
2443 	    pack->dot1x.ver, pack->dot1x.type,
2444 	    ntohs(pack->dot1x.len));
2445 
2446   /* Check that MAC address is our MAC, Broadcast or authentication MAC */
2447   if ((memcmp(pack->ethh.dst, this->ipif.hwaddr, PKT_ETH_ALEN)) &&
2448       (memcmp(pack->ethh.dst, bmac, PKT_ETH_ALEN)) &&
2449       (memcmp(pack->ethh.dst, amac, PKT_ETH_ALEN)))
2450     return 0;
2451 
2452   if (pack->dot1x.type == 1) { /* Start */
2453     struct dot1xpacket_t p;
2454     memset(&p, 0, sizeof(p));
2455 
2456     /* Allocate new connection */
2457     if (conn == NULL) {
2458       if (dhcp_newconn(this, &conn, pack->ethh.src))
2459 	return 0; /* Out of connections */
2460     }
2461 
2462     /* Ethernet header */
2463     memcpy(p.ethh.dst, pack->ethh.src, PKT_ETH_ALEN);
2464     memcpy(p.ethh.src, this->ipif.hwaddr, PKT_ETH_ALEN);
2465     p.ethh.prot = htons(PKT_ETH_PROTO_EAPOL);
2466 
2467     /* 802.1x header */
2468     p.dot1x.ver  = 1;
2469     p.dot1x.type = 0; /* EAP */
2470     p.dot1x.len =  htons(5);
2471 
2472     /* EAP Packet */
2473     p.eap.code      =  1;
2474     p.eap.id        =  1;
2475     p.eap.length    =  htons(5);
2476     p.eap.type      =  1; /* Identity */
2477 
2478     dhcp_senddot1x(conn, &p, PKT_ETH_HLEN + 4 + 5);
2479     return 0;
2480   }
2481   else if (pack->dot1x.type == 0) { /* EAP */
2482 
2483     /* TODO: Currently we only support authentications starting with a
2484        client sending a EAPOL start message. Need to also support
2485        authenticator initiated communications. */
2486     if (!conn)
2487       return 0;
2488 
2489     conn->lasttime = mainclock;
2490 
2491     if (this->cb_eap_ind)
2492       this->cb_eap_ind(conn, &pack->eap, ntohs(pack->eap.length));
2493 
2494     return 0;
2495   }
2496   else { /* Check for logoff */
2497     return 0;
2498   }
2499 }
2500 
2501 /**
2502  * dhcp_eapol_ind()
2503  * Call this function when a new EAPOL packet has arrived. This function
2504  * should be part of a select() loop in the application.
2505  **/
dhcp_eapol_ind(struct dhcp_t * this)2506 int dhcp_eapol_ind(struct dhcp_t *this) {
2507   struct dot1xpacket_t packet;
2508   ssize_t length;
2509 
2510   if ((length = net_read(&this->eapif, &packet, sizeof(packet))) < 0)
2511     return -1;
2512 
2513   if (options.debug) {
2514     struct pkt_ethhdr_t *ethh = &packet.ethh;
2515     log_dbg("eapol_decaps: dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x prot=%.4x",
2516 	    ethh->dst[0],ethh->dst[1],ethh->dst[2],ethh->dst[3],ethh->dst[4],ethh->dst[5],
2517 	    ethh->src[0],ethh->src[1],ethh->src[2],ethh->src[3],ethh->src[4],ethh->src[5],
2518 	    ntohs(ethh->prot));
2519   }
2520 
2521   return dhcp_receive_eapol(this, &packet);
2522 }
2523 
2524 
2525 /**
2526  * dhcp_set_cb_eap_ind()
2527  * Set callback function which is called when packet has arrived
2528  * Used for eap packets
2529  **/
dhcp_set_cb_eap_ind(struct dhcp_t * this,int (* cb_eap_ind)(struct dhcp_conn_t * conn,void * pack,size_t len))2530 int dhcp_set_cb_eap_ind(struct dhcp_t *this,
2531   int (*cb_eap_ind) (struct dhcp_conn_t *conn, void *pack, size_t len)) {
2532   this->cb_eap_ind = cb_eap_ind;
2533   return 0;
2534 }
2535 
2536 
2537 /**
2538  * dhcp_set_cb_data_ind()
2539  * Set callback function which is called when packet has arrived
2540  **/
dhcp_set_cb_data_ind(struct dhcp_t * this,int (* cb_data_ind)(struct dhcp_conn_t * conn,void * pack,size_t len))2541 int dhcp_set_cb_data_ind(struct dhcp_t *this,
2542   int (*cb_data_ind) (struct dhcp_conn_t *conn, void *pack, size_t len)) {
2543   this->cb_data_ind = cb_data_ind;
2544   return 0;
2545 }
2546 
2547 
2548 /**
2549  * dhcp_set_cb_data_ind()
2550  * Set callback function which is called when a dhcp request is received
2551  **/
dhcp_set_cb_request(struct dhcp_t * this,int (* cb_request)(struct dhcp_conn_t * conn,struct in_addr * addr,struct dhcp_fullpacket_t * pack,size_t len))2552 int dhcp_set_cb_request(struct dhcp_t *this,
2553   int (*cb_request) (struct dhcp_conn_t *conn, struct in_addr *addr, struct dhcp_fullpacket_t *pack, size_t len)) {
2554   this->cb_request = cb_request;
2555   return 0;
2556 }
2557 
2558 
2559 /**
2560  * dhcp_set_cb_connect()
2561  * Set callback function which is called when a connection is created
2562  **/
dhcp_set_cb_connect(struct dhcp_t * this,int (* cb_connect)(struct dhcp_conn_t * conn))2563 int dhcp_set_cb_connect(struct dhcp_t *this,
2564              int (*cb_connect) (struct dhcp_conn_t *conn)) {
2565   this->cb_connect = cb_connect;
2566   return 0;
2567 }
2568 
2569 /**
2570  * dhcp_set_cb_disconnect()
2571  * Set callback function which is called when a connection is deleted
2572  **/
dhcp_set_cb_disconnect(struct dhcp_t * this,int (* cb_disconnect)(struct dhcp_conn_t * conn,int term_cause))2573 int dhcp_set_cb_disconnect(struct dhcp_t *this,
2574   int (*cb_disconnect) (struct dhcp_conn_t *conn, int term_cause)) {
2575   this->cb_disconnect = cb_disconnect;
2576   return 0;
2577 }
2578 
dhcp_set_cb_getinfo(struct dhcp_t * this,int (* cb_getinfo)(struct dhcp_conn_t * conn,bstring b,int fmt))2579 int dhcp_set_cb_getinfo(struct dhcp_t *this,
2580   int (*cb_getinfo) (struct dhcp_conn_t *conn, bstring b, int fmt)) {
2581   this->cb_getinfo = cb_getinfo;
2582   return 0;
2583 }
2584 
2585 
2586 #if defined (__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__)
2587 
dhcp_receive(struct dhcp_t * this)2588 int dhcp_receive(struct dhcp_t *this) {
2589   ssize_t length = 0;
2590   size_t offset = 0;
2591   struct bpf_hdr *hdrp;
2592   struct pkt_ethhdr_t *ethhdr;
2593 
2594   if (this->rbuf_offset == this->rbuf_len) {
2595     length = net_read(&this->ipif, this->rbuf, this->rbuf_max);
2596 
2597     if (length <= 0)
2598       return length;
2599 
2600     this->rbuf_offset = 0;
2601     this->rbuf_len = length;
2602   }
2603 
2604   while (this->rbuf_offset != this->rbuf_len) {
2605 
2606     if (this->rbuf_len - this->rbuf_offset < sizeof(struct bpf_hdr)) {
2607       this->rbuf_offset = this->rbuf_len;
2608       continue;
2609     }
2610 
2611     hdrp = (struct bpf_hdr *) &this->rbuf[this->rbuf_offset];
2612 
2613     if (this->rbuf_offset + hdrp->bh_hdrlen + hdrp->bh_caplen >
2614 	this->rbuf_len) {
2615       this->rbuf_offset = this->rbuf_len;
2616       continue;
2617     }
2618 
2619     if (hdrp->bh_caplen != hdrp->bh_datalen) {
2620       this->rbuf_offset += hdrp->bh_hdrlen + hdrp->bh_caplen;
2621       continue;
2622     }
2623 
2624     ethhdr = (struct pkt_ethhdr_t *)
2625       (this->rbuf + this->rbuf_offset + hdrp->bh_hdrlen);
2626 
2627     switch (ntohs(ethhdr->prot)) {
2628     case PKT_ETH_PROTO_IP:
2629       dhcp_receive_ip(this, (struct pkt_ippacket_t*) ethhdr, hdrp->bh_caplen);
2630       break;
2631     case PKT_ETH_PROTO_ARP:
2632       dhcp_receive_arp(this, (struct arp_fullpacket_t*) ethhdr, hdrp->bh_caplen);
2633       break;
2634     case PKT_ETH_PROTO_EAPOL:
2635       dhcp_receive_eapol(this, (struct dot1xpacket_t*) ethhdr);
2636       break;
2637 
2638     default:
2639       break;
2640     }
2641     this->rbuf_offset += hdrp->bh_hdrlen + hdrp->bh_caplen;
2642   };
2643   return (0);
2644 }
2645 #endif
2646