• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/relayd/dhcp.c

  1 /*
  2  *   Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
  3  *
  4  *   This program is free software; you can redistribute it and/or modify
  5  *   it under the terms of the GNU General Public License v2 as published by
  6  *   the Free Software Foundation.
  7  *
  8  *   This program is distributed in the hope that it will be useful,
  9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11  *   GNU General Public License for more details.
 12  *
 13  *   You should have received a copy of the GNU General Public License
 14  *   along with this program; if not, write to the Free Software
 15  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 16  */
 17 
 18 #define _GNU_SOURCE
 19 #include <sys/socket.h>
 20 #include <stdio.h>
 21 #include <string.h>
 22 #include <errno.h>
 23 #include <unistd.h>
 24 #include <fcntl.h>
 25 
 26 #include "relayd.h"
 27 
 28 struct ip_packet {
 29         struct ether_header eth;
 30         struct iphdr iph;
 31 } __packed;
 32 
 33 
 34 enum {
 35         DHCP_OPTION_ROUTER = 0x03,
 36         DHCP_OPTION_ROUTES = 0x79,
 37         DHCP_OPTION_END = 0xff,
 38 };
 39 
 40 struct dhcp_option {
 41         uint8_t code;
 42         uint8_t len;
 43         uint8_t data[];
 44 };
 45 
 46 struct dhcp_header {
 47         uint8_t op, htype, hlen, hops;
 48         uint32_t xit;
 49         uint16_t secs, flags;
 50         struct in_addr ciaddr, yiaddr, siaddr, giaddr;
 51         unsigned char chaddr[16];
 52         unsigned char sname[64];
 53         unsigned char file[128];
 54         uint32_t cookie;
 55         uint8_t option_data[];
 56 } __packed;
 57 
 58 static uint16_t
 59 chksum(uint16_t sum, const uint8_t *data, uint16_t len)
 60 {
 61         const uint8_t *last;
 62         uint16_t t;
 63 
 64         last = data + len - 1;
 65 
 66         while(data < last) {
 67                 t = (data[0] << 8) + data[1];
 68                 sum += t;
 69                 if(sum < t)
 70                         sum++;
 71                 data += 2;
 72         }
 73 
 74         if(data == last) {
 75                 t = (data[0] << 8) + 0;
 76                 sum += t;
 77                 if(sum < t)
 78                         sum++;
 79         }
 80 
 81         return sum;
 82 }
 83 
 84 static void
 85 parse_dhcp_options(struct relayd_host *host, struct dhcp_header *dhcp, int len)
 86 {
 87         uint8_t *end = (uint8_t *) dhcp + len;
 88         struct dhcp_option *opt = (void *)dhcp->option_data;
 89         static const uint8_t dest[4] = { 0, 0, 0, 0 };
 90 
 91         while((uint8_t *) opt  + sizeof(*opt) < end) {
 92                 if ((uint8_t *) opt + opt->len > end ||
 93                     (uint8_t *) opt + sizeof(*opt) > end )
 94                         break;
 95 
 96                 opt = (void *) &opt->data[opt->len];
 97                 if ((uint8_t *) opt + sizeof(*opt) > end )
 98                         break;
 99                 switch(opt->code) {
100                 case DHCP_OPTION_ROUTER:
101                         DPRINTF(2, "Found a DHCP router option, len=%d\n", opt->len);
102                         if (!memcmp(opt->data, host->ipaddr, 4))
103                                 relayd_add_host_route(host, dest, 0);
104                         else
105                                 relayd_add_pending_route(opt->data, dest, 0, 10000);
106                         break;
107                 case DHCP_OPTION_ROUTES:
108                         DPRINTF(2, "Found a DHCP static routes option, len=%d\n", opt->len);
109                         break;
110                 case DHCP_OPTION_END:
111                         opt = (void *) end;
112                         continue;
113                 default:
114                         DPRINTF(3, "Skipping unknown DHCP option %02x\n", opt->code);
115                         continue;
116                 }
117 
118         }
119 }
120 
121 bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward, bool parse)
122 {
123         struct ip_packet *pkt = data;
124         struct udphdr *udp;
125         struct dhcp_header *dhcp;
126         struct relayd_host *host;
127         int udplen;
128         uint16_t sum;
129 
130         if (pkt->eth.ether_type != htons(ETH_P_IP))
131                 return false;
132 
133         if (pkt->iph.version != 4)
134                 return false;
135 
136         if (pkt->iph.protocol != IPPROTO_UDP)
137                 return false;
138 
139         udp = (void *) ((char *) &pkt->iph + (pkt->iph.ihl << 2));
140         dhcp = (void *) (udp + 1);
141 
142         if ((uint8_t *)udp  + sizeof(*udp)  > (uint8_t *)data + len ||
143             (uint8_t *)dhcp + sizeof(*dhcp) > (uint8_t *)data + len)
144                 return false;
145 
146         udplen = ntohs(udp->len);
147         if (udplen > len - ((char *) udp - (char *) data))
148                 return false;
149 
150         if (udp->dest != htons(67) && udp->source != htons(67))
151                 return false;
152 
153         if (dhcp->op != 1 && dhcp->op != 2)
154                 return false;
155 
156         if (!forward)
157                 return true;
158 
159         if (dhcp->op == 2) {
160                 host = relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr);
161                 if (host && parse)
162                         parse_dhcp_options(host, dhcp, udplen - sizeof(struct udphdr));
163         }
164 
165         DPRINTF(2, "%s: handling DHCP %s\n", rif->ifname, (dhcp->op == 1 ? "request" : "response"));
166 
167         dhcp->flags |= htons(DHCP_FLAG_BROADCAST);
168 
169         udp->check = 0;
170         sum = udplen + IPPROTO_UDP;
171         sum = chksum(sum, (void *) &pkt->iph.saddr, 8);
172         sum = chksum(sum, (void *) udp, udplen);
173         if (sum == 0)
174                 sum = 0xffff;
175 
176         udp->check = htons(~sum);
177 
178         relayd_forward_bcast_packet(rif, data, len);
179 
180         return true;
181 }
182 
183 
184 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt