+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;
+ }
+
+ md5_begin(&md5);
+ md5_hash(secretbytes, sizeof(secretbytes), &md5);
+ md5_hash(auth_o->key, sizeof(auth_o->key), &md5);
+ md5_end(auth_o->key, &md5);
+ } else {
+ dhcpv4_put(&fr_msg, &cookie, DHCPV4_OPT_SERVERID, 4,
+ &a->fr_ip->addr.addr.in.s_addr);
+ dhcpv4_put(&fr_msg, &cookie, DHCPV4_OPT_END, 0, NULL);
+ }
+
+ struct sockaddr_in dest;
+ memset(&dest, 0, sizeof(dest));
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons(DHCPV4_CLIENT_PORT);
+ dest.sin_addr.s_addr = a->addr;
+
+ syslog(LOG_WARNING, "sending %s to %02x:%02x:%02x:%02x:%02x:%02x - %s",
+ dhcpv4_msg_to_string(msg),
+ a->hwaddr[0], a->hwaddr[1], a->hwaddr[2],
+ a->hwaddr[3], a->hwaddr[4], a->hwaddr[5],
+ inet_ntoa(dest.sin_addr));
+
+ sendto(iface->dhcpv4_event.uloop.fd, &fr_msg, sizeof(fr_msg),
+ MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest));
+}
+
+static void dhcpv4_fr_timer(struct uloop_timeout *event)
+{
+ struct dhcpv4_assignment *a = container_of(event, struct dhcpv4_assignment, fr_timer);
+
+ if (a->fr_cnt > 0 && a->fr_cnt < 8) {
+ dhcpv4_fr_send(a);
+ uloop_timeout_set(&a->fr_timer, 1000 << a->fr_cnt);
+ a->fr_cnt++;
+ } else
+ dhcpv4_fr_stop(a);
+}
+
+static void dhcpv4_fr_start(struct dhcpv4_assignment *a)
+{
+ uloop_timeout_set(&a->fr_timer, 1000 << a->fr_cnt);
+ a->fr_timer.cb = dhcpv4_fr_timer;
+ a->fr_cnt++;
+
+ dhcpv4_fr_send(a);
+}
+
+static void dhcpv4_fr_stop(struct dhcpv4_assignment *a)
+{
+ uloop_timeout_cancel(&a->fr_timer);
+ decr_ref_cnt_ip(&a->fr_ip, a->iface);
+ a->fr_cnt = 0;
+ a->fr_timer.cb = NULL;
+}
+