X-Git-Url: http://git.archive.openwrt.org/?a=blobdiff_plain;f=dhcp.c;h=aefe34fd80e6c5ab8fe5bc19581e12954e0e782a;hb=5bd3096cf8309f11d07b8cce5e7a515f526c3674;hp=a144346dd2ab8422f981bccb1c5e697407eddf34;hpb=11104933cadd49b7618d30d451aa0e00508af4ff;p=project%2Frelayd.git diff --git a/dhcp.c b/dhcp.c index a144346..aefe34f 100644 --- a/dhcp.c +++ b/dhcp.c @@ -15,6 +15,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#define _GNU_SOURCE #include #include #include @@ -29,6 +30,19 @@ struct ip_packet { struct iphdr iph; } __packed; + +enum { + DHCP_OPTION_ROUTER = 0x03, + DHCP_OPTION_ROUTES = 0x79, + DHCP_OPTION_END = 0xff, +}; + +struct dhcp_option { + uint8_t code; + uint8_t len; + uint8_t data[]; +}; + struct dhcp_header { uint8_t op, htype, hlen, hops; uint32_t xit; @@ -37,6 +51,8 @@ struct dhcp_header { unsigned char chaddr[16]; unsigned char sname[64]; unsigned char file[128]; + uint32_t cookie; + uint8_t option_data[]; } __packed; static uint16_t @@ -65,11 +81,46 @@ chksum(uint16_t sum, const uint8_t *data, uint16_t len) return sum; } -bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward) +static void +parse_dhcp_options(struct relayd_host *host, struct dhcp_header *dhcp, int len) +{ + uint8_t *end = (uint8_t *) dhcp + len; + struct dhcp_option *opt = (void *)dhcp->option_data; + static const uint8_t dest[4] = { 0, 0, 0, 0 }; + + while((uint8_t *) opt < end) { + if ((uint8_t *) opt + opt->len > end) + break; + + opt = (void *) &opt->data[opt->len]; + switch(opt->code) { + case DHCP_OPTION_ROUTER: + DPRINTF(2, "Found a DHCP router option, len=%d\n", opt->len); + if (!memcmp(opt->data, host->ipaddr, 4)) + relayd_add_host_route(host, dest, 0); + else + relayd_add_pending_route(opt->data, dest, 0, 10000); + break; + case DHCP_OPTION_ROUTES: + DPRINTF(2, "Found a DHCP static routes option, len=%d\n", opt->len); + break; + case DHCP_OPTION_END: + opt = (void *) end; + continue; + default: + DPRINTF(3, "Skipping unknown DHCP option %02x\n", opt->code); + continue; + } + + } +} + +bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward, bool parse) { struct ip_packet *pkt = data; struct udphdr *udp; struct dhcp_header *dhcp; + struct relayd_host *host; int udplen; uint16_t sum; @@ -98,8 +149,11 @@ bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len if (!forward) return true; - if (dhcp->op == 2) - relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr); + if (dhcp->op == 2) { + host = relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr); + if (host && parse) + parse_dhcp_options(host, dhcp, udplen - sizeof(struct udphdr)); + } DPRINTF(2, "%s: handling DHCP %s\n", rif->ifname, (dhcp->op == 1 ? "request" : "response"));