Semi-decennial update. 50% code inflation.
[cbaos.git] / net / dhcp.c
blob0e6ec06e22747de9abf15676de88e612d26a0cbb
1 /* Author: Domen Puncer Kugler <domen@cba.si>. License: WTFPL, see file LICENSE */
3 #include <errno.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <string.h>
8 #include <endianess.h>
9 #include <sched.h>
10 #include <timekeeping.h>
11 #include <types.h>
12 #include <net/dhcp.h>
13 #include <net/netpacket.h>
17 * DHCP summary
18 * client: DISCOVER 01 01 06 00 XID 0000 0000 4x00000000 CMAC(16B) 0(192B)
19 * 63825363(MAGIC) dhcp options: 53 1 1 0xff
20 * (UDP: src=0, sport=68, dest=255, dport=67)
21 * server: OFFER 02 01 06 00 XID 0000 0000 0 YIA(your IP) SIA(server IP) GIA CMAC(16B) 0(192B)
22 * 63825363(MAGIC) dhcp options: 53 1 2... 0xff
23 * (UDP: src=<ip>, sport=67, dest=255, dport=68)
24 * client: REQUEST 01 01 06 00 XID 0000 0000 0 0 SIA(server IP) GIA CMAC(16B) (192B)
25 * 63825363(MAGIC) dhcp options: 53 1 3, 54 4 SIA, 50 4 YIA, 0xff
26 * (UDP: src=0, sport=68, dest=255, dport=67)
27 * server: ACK 02 01 06 00 XID 0000 0000 0 YIA(your IP) SIA(server IP) GIA CMAC(16B) 0(192B)
28 * 63825363(MAGIC) dhcp options: 53 1 5... 0xff
29 * (UDP: src=<ip>, sport=67, dest=255, dport=68)
32 #define DHCP_STATE_INACTIVE 0
33 #define DHCP_STATE_DISCOVER 1
34 #define DHCP_STATE_REQUEST 2
35 #define DHCP_STATE_FINISHED 3
37 #define DHCP_MAGIC 0x63825363
40 static struct {
41 int state;
42 u32 xid;
43 u32 last_op_time;
44 u32 renewal_time;
45 } dhcp_state;
47 int dhcp_negotiate_discover(void)
49 const int len = sizeof(struct dhcp_packet)+4; /* 4B for options */
50 struct netpacket *packet;
51 int ret = netpacket_alloc_and_prepare_udp(&packet, ip_bcast, 68, 67, len);
52 if (ret < 0)
53 return ret;
54 struct dhcp_packet *dhcp = &packet->dhcp;
56 memset(&dhcp->op, 0, offsetof(struct dhcp_packet, magic));
57 dhcp->op = 1;
58 dhcp->htype = 1;
59 dhcp->hlen = 6;
60 dhcp_state.xid = get_be32(&netconfig.mac[2]); // should be random per dhcp session
61 put_be32(dhcp->xid, dhcp_state.xid);
62 memcpy(dhcp->cmac, netconfig.mac, sizeof(mac_t));
63 put_be32(dhcp->magic, DHCP_MAGIC);
65 dhcp->options[0] = DHCP_OPT_DHCP_MESSAGE;
66 dhcp->options[1] = 1;
67 dhcp->options[2] = DHCP_MESSAGE_DISCOVER;
68 dhcp->options[3] = DHCP_OPT_END;
70 printf("DHCP: sending DISCOVER (state: %d)\n", dhcp_state.state);
71 dhcp_state.last_op_time = time();
72 ret = netpacket_send(packet);
74 if (ret == 0)
75 dhcp_state.state = DHCP_STATE_DISCOVER;
76 return ret;
79 int dhcp_negotiate_request(ip_t siaddr, ip_t yiaddr)
81 const int len = sizeof(struct dhcp_packet)+16; /* 3+6+6+1 B for options */
82 struct netpacket *packet;
83 int ret = netpacket_alloc_and_prepare_udp(&packet, ip_bcast, 68, 67, len);
84 if (ret < 0)
85 return ret;
86 struct dhcp_packet *dhcp = &packet->dhcp;
88 memset(&dhcp->op, 0, offsetof(struct dhcp_packet, magic));
89 dhcp->op = 1;
90 dhcp->htype = 1;
91 dhcp->hlen = 6;
92 put_be32(dhcp->xid, dhcp_state.xid);
93 memcpy(dhcp->siaddr, siaddr, sizeof(ip_t));
94 memcpy(dhcp->cmac, netconfig.mac, sizeof(mac_t));
95 put_be32(dhcp->magic, DHCP_MAGIC);
97 int pos = 0;
98 dhcp->options[pos++] = DHCP_OPT_DHCP_MESSAGE;
99 dhcp->options[pos++] = 1;
100 dhcp->options[pos++] = DHCP_MESSAGE_REQUEST;
102 dhcp->options[pos++] = DHCP_OPT_SIADDR;
103 dhcp->options[pos++] = sizeof(ip_t);
104 memcpy(&dhcp->options[pos], siaddr, sizeof(ip_t));
105 pos += sizeof(ip_t);
107 dhcp->options[pos++] = DHCP_OPT_YIADDR;
108 dhcp->options[pos++] = sizeof(ip_t);
109 memcpy(&dhcp->options[pos], yiaddr, sizeof(ip_t));
110 pos += sizeof(ip_t);
112 dhcp->options[pos++] = DHCP_OPT_END;
114 printf("DHCP: sending REQUEST\n");
115 dhcp_state.last_op_time = time();
116 ret = netpacket_send(packet);
118 if (ret == 0)
119 dhcp_state.state = DHCP_STATE_REQUEST;
120 return ret;
123 static int dhcp_handle(u8 *buf, unsigned len)
125 struct dhcp_packet *dhcp = (struct dhcp_packet *)buf;
127 if (len < sizeof(*dhcp)+3) /* at least for DHCP_MESSAGE option */
128 return -EINVAL;
129 if (get_be32(dhcp->xid) != dhcp_state.xid)
130 return -EINVAL;
131 if (get_be32(dhcp->magic) != DHCP_MAGIC)
132 return -EINVAL;
133 if (dhcp->options[0] != DHCP_OPT_DHCP_MESSAGE || dhcp->options[1] != 1)
134 return -EINVAL;
136 if (dhcp->options[2] == DHCP_MESSAGE_OFFER && dhcp_state.state == DHCP_STATE_DISCOVER) {
137 #if 0
138 ip_t *siaddr = NULL;
139 ip_t *yiaddr = NULL;
141 /* option parsing */
142 pos = 3;
143 while (sizeof(*dhcp)+pos < len && dhcp->options[pos] != DHCP_OPT_END) {
144 if (dhcp->options[pos] == DHCP_OPT_SIADDR)
145 siaddr = &dhcp->options[pos+2];
146 if (dhcp->options[pos] == DHCP_OPT_YIADDR)
147 yiaddr = &dhcp->options[pos+2];
148 pos += 2+dhcp->options[pos+1];
151 if (!siaddr || !yiaddr) {
152 printf("error: dhcpd did not provide yia or sia\n");
153 dhcp_state.state = DHCP_STATE_INACTIVE;
154 return -EINVAL;
156 return dhcp_negotiate_request(*siaddr, *yiaddr);
157 #endif
158 printf("DHCP: received OFFER\n");
159 return dhcp_negotiate_request(dhcp->siaddr, dhcp->yiaddr);
160 } else
161 if (dhcp->options[2] == DHCP_MESSAGE_ACK && dhcp_state.state == DHCP_STATE_REQUEST) {
162 int renew = 6*3600;
163 #if 1
164 /* option parsing */
165 int pos = 3;
166 while (sizeof(*dhcp)+pos < len && dhcp->options[pos] != DHCP_OPT_END) {
167 if (dhcp->options[pos] == DHCP_OPT_IP_LEASE_TIME)
168 renew = get_be32(&dhcp->options[pos+2]) / 2;
169 pos += 2+dhcp->options[pos+1];
171 #endif
172 memcpy(netconfig.ip, dhcp->yiaddr, sizeof(ip_t));
173 memcpy(netconfig.server_ip, netconfig.ip, sizeof(ip_t)); // XXX hack
174 netconfig.server_ip[3] = 2;
176 printf("DHCP: received ACK, my IP:%d.%d.%d.%d, renew in %ds\n", netconfig.ip[0], netconfig.ip[1], netconfig.ip[2], netconfig.ip[3], renew);
177 dhcp_state.renewal_time = renew+time();
178 dhcp_state.state = DHCP_STATE_FINISHED;
179 netconfig.state = NET_IP; // XXX never becomes reset
180 return 0;
182 return -EINVAL;
185 void dhcp_work(void)
187 u32 now = time();
189 if (dhcp_state.state == DHCP_STATE_INACTIVE || (dhcp_state.state != DHCP_STATE_FINISHED && time_after_eq(now, dhcp_state.last_op_time+10))) {
190 printf("DHCP get ip; last:%d, now:%d\n", dhcp_state.last_op_time, now);
191 dhcp_negotiate_discover();
192 return;
195 if (dhcp_state.state == DHCP_STATE_FINISHED && time_after_eq(now, dhcp_state.renewal_time)) {
196 printf("DHCP renew\n");
197 dhcp_negotiate_discover();
198 return;
204 static struct udp_handler udp_handler_dhcp = {
205 .list = LIST_INIT(udp_handler_dhcp.list),
206 .port = 68,
207 .handler = dhcp_handle,
210 int dhcp_init(void)
212 return udp_register_handler(&udp_handler_dhcp);