1 /* Author: Domen Puncer Kugler <domen@cba.si>. License: WTFPL, see file LICENSE */
10 #include <timekeeping.h>
13 #include <net/netpacket.h>
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
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
);
54 struct dhcp_packet
*dhcp
= &packet
->dhcp
;
56 memset(&dhcp
->op
, 0, offsetof(struct dhcp_packet
, magic
));
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
;
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
);
75 dhcp_state
.state
= DHCP_STATE_DISCOVER
;
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
);
86 struct dhcp_packet
*dhcp
= &packet
->dhcp
;
88 memset(&dhcp
->op
, 0, offsetof(struct dhcp_packet
, magic
));
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
);
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
));
107 dhcp
->options
[pos
++] = DHCP_OPT_YIADDR
;
108 dhcp
->options
[pos
++] = sizeof(ip_t
);
109 memcpy(&dhcp
->options
[pos
], yiaddr
, 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
);
119 dhcp_state
.state
= DHCP_STATE_REQUEST
;
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 */
129 if (get_be32(dhcp
->xid
) != dhcp_state
.xid
)
131 if (get_be32(dhcp
->magic
) != DHCP_MAGIC
)
133 if (dhcp
->options
[0] != DHCP_OPT_DHCP_MESSAGE
|| dhcp
->options
[1] != 1)
136 if (dhcp
->options
[2] == DHCP_MESSAGE_OFFER
&& dhcp_state
.state
== DHCP_STATE_DISCOVER
) {
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
;
156 return dhcp_negotiate_request(*siaddr
, *yiaddr
);
158 printf("DHCP: received OFFER\n");
159 return dhcp_negotiate_request(dhcp
->siaddr
, dhcp
->yiaddr
);
161 if (dhcp
->options
[2] == DHCP_MESSAGE_ACK
&& dhcp_state
.state
== DHCP_STATE_REQUEST
) {
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];
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
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();
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();
204 static struct udp_handler udp_handler_dhcp
= {
205 .list
= LIST_INIT(udp_handler_dhcp
.list
),
207 .handler
= dhcp_handle
,
212 return udp_register_handler(&udp_handler_dhcp
);