+static void dhcpv4_fr_send(struct dhcpv4_assignment *a)
+{
+ struct dhcpv4_message fr_msg = {
+ .op = DHCPV4_BOOTREPLY,
+ .htype = 1,
+ .hlen = 6,
+ .hops = 0,
+ .secs = 0,
+ .flags = 0,
+ .ciaddr = {INADDR_ANY},
+ .yiaddr = {INADDR_ANY},
+ .siaddr = {INADDR_ANY},
+ .giaddr = {INADDR_ANY},
+ .chaddr = {0},
+ .sname = {0},
+ .file = {0},
+ };
+ struct dhcpv4_auth_forcerenew *auth_o, auth = {
+ .protocol = 3,
+ .algorithm = 1,
+ .rdm = 0,
+ .replay = {htonl(time(NULL)), htonl(++serial)},
+ .type = 2,
+ .key = {0},
+ };
+ struct interface *iface = a->iface;
+
+ odhcpd_urandom(&fr_msg.xid, sizeof(fr_msg.xid));
+ memcpy(fr_msg.chaddr, a->hwaddr, fr_msg.hlen);
+
+ fr_msg.options[0] = 0x63;
+ fr_msg.options[1] = 0x82;
+ fr_msg.options[2] = 0x53;
+ fr_msg.options[3] = 0x63;
+
+ uint8_t *cookie = &fr_msg.options[4];
+ uint8_t msg = DHCPV4_MSG_FORCERENEW;
+
+ dhcpv4_put(&fr_msg, &cookie, DHCPV4_OPT_MESSAGE, 1, &msg);
+ if (a->accept_fr_nonce) {
+ dhcpv4_put(&fr_msg, &cookie, DHCPV4_OPT_AUTHENTICATION, sizeof(auth), &auth);
+ auth_o = (struct dhcpv4_auth_forcerenew *)(cookie - sizeof(auth));
+ dhcpv4_put(&fr_msg, &cookie, DHCPV4_OPT_END, 0, NULL);
+
+ md5_ctx_t md5;
+ uint8_t secretbytes[64];
+ memset(secretbytes, 0, sizeof(secretbytes));
+ memcpy(secretbytes, a->key, sizeof(a->key));
+
+ for (size_t i = 0; i < sizeof(secretbytes); ++i)
+ secretbytes[i] ^= 0x36;
+
+ md5_begin(&md5);
+ md5_hash(secretbytes, sizeof(secretbytes), &md5);
+ md5_hash(&fr_msg, sizeof(fr_msg), &md5);
+ md5_end(auth_o->key, &md5);
+
+ for (size_t i = 0; i < sizeof(secretbytes); ++i) {
+ secretbytes[i] ^= 0x36;
+ secretbytes[i] ^= 0x5c;
+ }