b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright (C) 2013 Intel Corporation. All rights reserved.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright (C) 2014 Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
99634696183dfabae20104e58157c69029a11594Tom Gundersen/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
99634696183dfabae20104e58157c69029a11594Tom Gundersen * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
99634696183dfabae20104e58157c69029a11594Tom Gundersen * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
99634696183dfabae20104e58157c69029a11594Tom Gundersen * accidentally hand it out */
99634696183dfabae20104e58157c69029a11594Tom Gundersenint sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(address->s_addr != INADDR_ANY, -EINVAL);
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(server->address == INADDR_ANY, -EBUSY);
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
99634696183dfabae20104e58157c69029a11594Tom Gundersen server_off = be32toh(address->s_addr & ~netmask);
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* the server address cannot be the subnet address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* nor the broadcast address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(server_off != broadcast_off, -ERANGE);
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* 0 offset means we should set a default, we skip the first (subnet) address
99634696183dfabae20104e58157c69029a11594Tom Gundersen and take the next one */
99634696183dfabae20104e58157c69029a11594Tom Gundersen size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
99634696183dfabae20104e58157c69029a11594Tom Gundersen - offset /* exclude the addresses before the offset */
99634696183dfabae20104e58157c69029a11594Tom Gundersen - 1; /* exclude the last (broadcast) address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* The pool must contain at least one address */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->bound_leases = new0(DHCPLease*, size);
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (server_off >= offset && server_off - offset < size)
99634696183dfabae20104e58157c69029a11594Tom Gundersen server->bound_leases[server_off - offset] = &server->invalid_lease;
04c0136989b7eb896bfb0fb176e11233d69e1453Lennart Poetteringint sd_dhcp_server_is_running(sd_dhcp_server *server) {
756775814cf69471f74ce853745bba69f2ba94efThomas Hindoe Paaboel Andersen assert_return(server, false);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersensd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersenvoid client_id_hash_func(const void *p, struct siphash *state) {
1e2527a6fede996a429bd44b30a15e76ee293437Tom Gundersen siphash24_compress(&id->length, sizeof(id->length), state);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen siphash24_compress(id->data, id->length, state);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenint client_id_compare_func(const void *_a, const void *_b) {
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidtstatic const struct hash_ops client_id_hash_ops = {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenstatic void dhcp_lease_free(DHCPLease *lease) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersensd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering while ((lease = hashmap_steal_first(server->leases_by_client_id)))
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering hashmap_free(server->leases_by_client_id);
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersenint sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenint sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenint sd_dhcp_server_detach_event(sd_dhcp_server *server) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server->event = sd_event_unref(server->event);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersensd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenint sd_dhcp_server_stop(sd_dhcp_server *server) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sd_event_source_unref(server->receive_message);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen /* we attach source interface and address info to the message
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen rather than binding the socket. This will be mostly useful
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen when we gain support for arbitrary number of server addresses
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen pktinfo->ipi_spec_dst.s_addr = server->address;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic bool requested_broadcast(DHCPRequest *req) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenint dhcp_server_send_packet(sd_dhcp_server *server,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen /* RFC 2131 Section 4.1
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen If the ’giaddr’ field in a DHCP message from a client is non-zero,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen the server sends any return messages to the ’DHCP server’ port on the
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen field is zero and the ’ciaddr’ field is nonzero, then the server
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen set, then the server broadcasts DHCPOFFER and DHCPACK messages to
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen messages to the client’s hardware address and ’yiaddr’ address. In
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen messages to 0xffffffff.
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen Section 4.3.2
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen different subnet. The server MUST set the broadcast bit in the
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen client, because the client may not have a correct network address
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen or subnet mask, and the client may not be answering ARP requests.
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen } else if (req->message->ciaddr && type != DHCP_NAK)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return dhcp_server_send_udp(server, destination, &packet->dhcp,
d6bd972d061af306ede2affd2c9340a1660f7996Tom Gundersen else if (requested_broadcast(req) || type == DHCP_NAK)
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen return dhcp_server_send_udp(server, INADDR_BROADCAST,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen /* we cannot send UDP packet to specific MAC address when the
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen address is not yet configured, so must fall back to raw
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return dhcp_server_send_unicast_raw(server, packet,
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersenstatic int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams be32toh(req->message->xid), type, ARPHRD_ETHER,
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_ROUTER, 4, &server->address);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_ROUTER, 4, &server->address);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering sizeof(struct in_addr) * server->n_dns, server->dns);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering sizeof(struct in_addr) * server->n_ntp, server->ntp);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering strlen(server->timezone), server->timezone);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersenstatic int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = dhcp_server_send_udp(server, address, &packet->dhcp,
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poetteringstatic int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersenstatic void dhcp_request_free(DHCPRequest *req) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom GundersenDEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringstatic int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
e2acdb6b0f68d9b4152708a9f21bf9e11f8b9e7eTorstein Husebø /* set client id based on MAC address if client did not send an explicit
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering req->lifetime = MAX(1ULL, server->default_lease_time);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenstatic int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
99634696183dfabae20104e58157c69029a11594Tom Gundersen be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
99634696183dfabae20104e58157c69029a11594Tom Gundersen return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersenint dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
f693e9b38f13575b988335e3324a50dcc8772d48Tom Gundersen type = dhcp_option_parse(message, length, parse_request, req, &error_message);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering r = ensure_sane_request(server, req, message);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen /* this only fails on critical errors */
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen existing_lease = hashmap_get(server->leases_by_client_id,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* no pool allocated */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* for now pick a random free address from the pool */
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen /* even with no persistence of leases, we try to offer the same client
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen the same IP address. we do this by using the hash of the client id
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen as the offset into the pool of leases when finding the next free one */
99634696183dfabae20104e58157c69029a11594Tom Gundersen address = server->subnet | htobe32(server->pool_offset + next_offer);
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen next_offer = (next_offer + 1) % server->pool_size;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* no free addresses left */
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen /* this only fails on critical errors */
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_dhcp_server(server, "could not send offer: %s",
f693e9b38f13575b988335e3324a50dcc8772d48Tom Gundersen log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen /* TODO: make sure we don't offer this address again */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* see RFC 2131, section 4.3.2 */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* SELECTING */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* client did not pick us */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this MUST be zero */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this must be filled in with the yiaddr
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen from the chosen OFFER */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* INIT-REBOOT */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this MUST be zero */
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen /* TODO: check more carefully if IP is correct */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* REBINDING / RENEWING */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this MUST be filled in with clients IP address */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen pool_offset = get_pool_offset(server, address);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* verify that the requested address is from the pool, and either
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen owned by the current client or free */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->bound_leases[pool_offset] == existing_lease) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->client_id.data = memdup(req->client_id.data,
02557f973aed0fed7154fefe53d67e2935f918dcThomas Hindoe Paaboel Andersen if (!lease->client_id.data) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->client_id.length = req->client_id.length;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this only fails on critical errors */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "could not send ack: %s",
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen /* this only fails on critical errors */
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen log_dhcp_server(server, "could not send nak: %s",
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen if (existing_lease->address != req->message->ciaddr)
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen pool_offset = get_pool_offset(server, req->message->ciaddr);
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen if (server->bound_leases[pool_offset] == existing_lease) {
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen hashmap_remove(server->leases_by_client_id, existing_lease);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenstatic int server_receive_message(sd_event_source *s, int fd,
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen else if (buflen < 0)
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen /* TODO figure out if this can be done as a filter on
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen * the socket, like for IPv6 */
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering if (server->ifindex != info->ipi_ifindex)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return dhcp_server_handle_message(server, message, (size_t)len);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenint sd_dhcp_server_start(sd_dhcp_server *server) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_return(!server->receive_message, -EBUSY);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = sd_event_add_io(server->event, &server->receive_message,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = sd_event_source_set_priority(server->receive_message,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersenint sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (!lease || lease == &server->invalid_lease)
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = server_send_forcerenew(server, lease->address,
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersenint sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersen assert_return(timezone_is_valid(tz), -EINVAL);
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersen if (streq_ptr(tz, server->timezone))
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersen r = free_and_strdup(&server->timezone, tz);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringint sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringint sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poetteringint sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poetteringint sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)