Split DHCP code off into a separate source code file
[project/relayd.git] / 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 #include <sys/socket.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "relayd.h"
26
27 struct ip_packet {
28 struct ether_header eth;
29 struct iphdr iph;
30 } __packed;
31
32 struct dhcp_header {
33 uint8_t op, htype, hlen, hops;
34 uint32_t xit;
35 uint16_t secs, flags;
36 struct in_addr ciaddr, yiaddr, siaddr, giaddr;
37 unsigned char chaddr[16];
38 unsigned char sname[64];
39 unsigned char file[128];
40 } __packed;
41
42 static uint16_t
43 chksum(uint16_t sum, const uint8_t *data, uint16_t len)
44 {
45 const uint8_t *last;
46 uint16_t t;
47
48 last = data + len - 1;
49
50 while(data < last) {
51 t = (data[0] << 8) + data[1];
52 sum += t;
53 if(sum < t)
54 sum++;
55 data += 2;
56 }
57
58 if(data == last) {
59 t = (data[0] << 8) + 0;
60 sum += t;
61 if(sum < t)
62 sum++;
63 }
64
65 return sum;
66 }
67
68 bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward)
69 {
70 struct ip_packet *pkt = data;
71 struct udphdr *udp;
72 struct dhcp_header *dhcp;
73 int udplen;
74 uint16_t sum;
75
76 if (pkt->eth.ether_type != htons(ETH_P_IP))
77 return false;
78
79 if (pkt->iph.version != 4)
80 return false;
81
82 if (pkt->iph.protocol != IPPROTO_UDP)
83 return false;
84
85 udp = (void *) ((char *) &pkt->iph + (pkt->iph.ihl << 2));
86 dhcp = (void *) (udp + 1);
87
88 udplen = ntohs(udp->len);
89 if (udplen > len - ((char *) udp - (char *) data))
90 return false;
91
92 if (udp->dest != htons(67) && udp->source != htons(67))
93 return false;
94
95 if (dhcp->op != 1 && dhcp->op != 2)
96 return false;
97
98 if (!forward)
99 return true;
100
101 if (dhcp->op == 2)
102 relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr);
103
104 DPRINTF(2, "%s: handling DHCP %s\n", rif->ifname, (dhcp->op == 1 ? "request" : "response"));
105
106 dhcp->flags |= htons(DHCP_FLAG_BROADCAST);
107
108 udp->check = 0;
109 sum = udplen + IPPROTO_UDP;
110 sum = chksum(sum, (void *) &pkt->iph.saddr, 8);
111 sum = chksum(sum, (void *) udp, udplen);
112 if (sum == 0)
113 sum = 0xffff;
114
115 udp->check = htons(~sum);
116
117 relayd_forward_bcast_packet(rif, data, len);
118
119 return true;
120 }
121
122