X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp-client.c;h=2906f2cb3a018de58c11f0ffc161fb2efdc9cb6e;hp=68bce5e45959c7a9ca0878ecffeff76ecf7b4ef3;hb=d8d74ef06fa0ccf39084a6177e70e4c2297cca57;hpb=0af03ba57f43b973463e15949c2830cbd228f19d diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 68bce5e45..2906f2cb3 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include "dhcp-lease-internal.h" #include "sd-dhcp-client.h" +#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN + struct sd_dhcp_client { RefCount n_ref; @@ -57,11 +60,14 @@ struct sd_dhcp_client { uint8_t type; struct ether_addr mac_addr; } _packed_ client_id; + uint8_t mac_addr[MAX_MAC_ADDR_LEN]; + size_t mac_addr_len; + uint16_t arp_type; char *hostname; char *vendor_class_identifier; + uint32_t mtu; uint32_t xid; usec_t start_time; - uint16_t secs; unsigned int attempt; usec_t request_sent; sd_event_source *timeout_t1; @@ -162,15 +168,25 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) { return 0; } -int sd_dhcp_client_set_mac(sd_dhcp_client *client, - const struct ether_addr *addr) { +int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; assert_return(client, -EINVAL); assert_return(addr, -EINVAL); + assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(arp_type > 0, -EINVAL); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; - if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0) + if (client->mac_addr_len == addr_len && + memcmp(&client->mac_addr, addr, addr_len) == 0) return 0; if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { @@ -180,6 +196,10 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, client_stop(client, DHCP_EVENT_STOP); } + memcpy(&client->mac_addr, addr, addr_len); + client->mac_addr_len = addr_len; + client->arp_type = arp_type; + memcpy(&client->client_id.mac_addr, addr, ETH_ALEN); client->client_id.type = 0x01; @@ -227,6 +247,15 @@ int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, return 0; } +int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { + assert_return(client, -EINVAL); + assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE); + + client->mtu = mtu; + + return 0; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); assert_return(ret, -EINVAL); @@ -291,10 +320,12 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, _cleanup_free_ DHCPPacket *packet; size_t optlen, optoffset, size; be16_t max_size; + usec_t time_now; + uint16_t secs; int r; assert(client); - assert(client->secs); + assert(client->start_time); assert(ret); assert(_optlen); assert(_optoffset); @@ -308,13 +339,21 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, - optlen, &optoffset); + client->arp_type, optlen, &optoffset); if (r < 0) return r; /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers refuse to issue an DHCP lease if 'secs' is set to zero */ - packet->dhcp.secs = htobe16(client->secs); + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + assert(time_now >= client->start_time); + + /* seconds between sending first and last DISCOVER + * must always be strictly positive to deal with broken servers */ + secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; + packet->dhcp.secs = htobe16(secs); /* RFC2132 section 4.1 A client that cannot receive unicast IP datagrams until its protocol @@ -327,14 +366,17 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, Note: some interfaces needs this to be enabled, but some networks needs this to be disabled as broadcasts are filteretd, so this needs to be configurable */ - if (client->request_broadcast) + if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) packet->dhcp.flags = htobe16(0x8000); /* RFC2132 section 4.1.1: The client MUST include its hardware address in the ’chaddr’ field, if - necessary for delivery of DHCP reply messages. + necessary for delivery of DHCP reply messages. Non-Ethernet + interfaces will leave 'chaddr' empty and use the client identifier + instead (eg, RFC 4390 section 2.1). */ - memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN); + if (client->arp_type == ARPHRD_ETHER) + memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); /* Some DHCP servers will refuse to issue an DHCP lease if the Client Identifier option is not set */ @@ -366,9 +408,23 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, Note (from ConnMan): Some DHCP servers will send bigger DHCP packets than the defined default size unless the Maximum Messge Size option is explicitely set + + RFC3442 "Requirements to Avoid Sizing Constraints": + Because a full routing table can be quite large, the standard 576 + octet maximum size for a DHCP message may be too short to contain + some legitimate Classless Static Route options. Because of this, + clients implementing the Classless Static Route option SHOULD send a + Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP + stack is capable of receiving larger IP datagrams. In this case, the + client SHOULD set the value of this option to at least the MTU of the + interface that the client is configuring. The client MAY set the + value of this option higher, up to the size of the largest UDP packet + it is prepared to accept. (Note that the value specified in the + Maximum DHCP Message Size option is the total maximum packet size, + including IP and UDP headers.) */ max_size = htobe16(size); - r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, 2, &max_size); if (r < 0) @@ -394,24 +450,12 @@ static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet, static int client_send_discover(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *discover = NULL; size_t optoffset, optlen; - usec_t time_now; int r; assert(client); assert(client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_SELECTING); - /* See RFC2131 section 4.4.1 */ - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - assert(time_now >= client->start_time); - - /* seconds between sending first and last DISCOVER - * must always be strictly positive to deal with broken servers */ - client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; - r = client_message_init(client, &discover, DHCP_DISCOVER, &optlen, &optoffset); if (r < 0) @@ -676,6 +720,10 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, if (r < 0) goto error; + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + if (r < 0) + goto error; + switch (client->state) { case DHCP_STATE_INIT: r = client_send_discover(client); @@ -749,6 +797,10 @@ static int client_initialize_io_events(sd_dhcp_client *client, if (r < 0) goto error; + r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message"); + if (r < 0) + goto error; + error: if (r < 0) client_stop(client, r); @@ -775,6 +827,10 @@ static int client_initialize_time_events(sd_dhcp_client *client) { r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + if (r < 0) + goto error; + error: if (r < 0) client_stop(client, r); @@ -804,17 +860,17 @@ static int client_start(sd_dhcp_client *client) { client->xid = random_u32(); - r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr); + r = dhcp_network_bind_raw_socket(client->index, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); if (r < 0) { client_stop(client, r); return r; } client->fd = r; - if (client->state == DHCP_STATE_INIT) { + if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT) client->start_time = now(clock_boottime_or_monotonic()); - client->secs = 0; - } return client_initialize_events(client, client_receive_message_raw); } @@ -848,7 +904,9 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->state = DHCP_STATE_REBINDING; client->attempt = 1; - r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr); + r = dhcp_network_bind_raw_socket(client->index, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); if (r < 0) { client_stop(client, r); return 0; @@ -862,7 +920,6 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; DHCP_CLIENT_DONT_DESTROY(client); - int r; client->state = DHCP_STATE_RENEWING; client->attempt = 1; @@ -881,7 +938,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease); if (r != DHCP_OFFER) { - log_dhcp_client(client, "receieved message was not an OFFER, ignoring"); + log_dhcp_client(client, "received message was not an OFFER, ignoring"); return -ENOMSG; } @@ -892,7 +949,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, if (lease->address == INADDR_ANY || lease->server_address == INADDR_ANY || lease->lifetime == 0) { - log_dhcp_client(client, "receieved lease lacks address, server " + log_dhcp_client(client, "received lease lacks address, server " "address or lease lifetime, ignoring"); return -ENOMSG; } @@ -900,7 +957,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, if (lease->subnet_mask == INADDR_ANY) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { - log_dhcp_client(client, "receieved lease lacks subnet " + log_dhcp_client(client, "received lease lacks subnet " "mask, and a fallback one can not be " "generated, ignoring"); return -ENOMSG; @@ -916,6 +973,19 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, return 0; } +static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, + size_t len) { + int r; + + r = dhcp_option_parse(force, len, NULL, NULL); + if (r != DHCP_FORCERENEW) + return -ENOMSG; + + log_dhcp_client(client, "FORCERENEW"); + + return 0; +} + static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; @@ -932,7 +1002,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, } if (r != DHCP_ACK) { - log_dhcp_client(client, "receieved message was not an ACK, ignoring"); + log_dhcp_client(client, "received message was not an ACK, ignoring"); return -ENOMSG; } @@ -943,7 +1013,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, if (lease->address == INADDR_ANY || lease->server_address == INADDR_ANY || lease->lifetime == 0) { - log_dhcp_client(client, "receieved lease lacks address, server " + log_dhcp_client(client, "received lease lacks address, server " "address or lease lifetime, ignoring"); return -ENOMSG; } @@ -951,7 +1021,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, if (lease->subnet_mask == INADDR_ANY) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { - log_dhcp_client(client, "receieved lease lacks subnet " + log_dhcp_client(client, "received lease lacks subnet " "mask, and a fallback one can not be " "generated, ignoring"); return -ENOMSG; @@ -1071,6 +1141,10 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; + r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime"); + if (r < 0) + return r; + log_dhcp_client(client, "lease expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, 0)); @@ -1094,6 +1168,10 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; + r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout"); + if (r < 0) + return r; + log_dhcp_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, 0)); @@ -1116,6 +1194,10 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; + r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer"); + if (r < 0) + return r; + log_dhcp_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, 0)); @@ -1156,6 +1238,10 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, client->event_priority); if (r < 0) goto error; + + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + if (r < 0) + goto error; } else if (r == -ENOMSG) /* invalid message, let's ignore it */ return 0; @@ -1171,6 +1257,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, if (r >= 0) { client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->receive_message = + sd_event_source_unref(client->receive_message); + client->fd = asynchronous_close(client->fd); if (IN_SET(client->state, DHCP_STATE_REQUESTING, DHCP_STATE_REBOOTING)) @@ -1226,9 +1315,20 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, break; + case DHCP_STATE_BOUND: + r = client_handle_forcerenew(client, message, len); + if (r >= 0) { + r = client_timeout_t1(NULL, 0, client); + if (r < 0) + goto error; + } else if (r == -ENOMSG) + /* invalid message, let's ignore it */ + return 0; + + break; + case DHCP_STATE_INIT: case DHCP_STATE_INIT_REBOOT: - case DHCP_STATE_BOUND: break; @@ -1249,6 +1349,9 @@ static int client_receive_message_udp(sd_event_source *s, int fd, sd_dhcp_client *client = userdata; _cleanup_free_ DHCPMessage *message = NULL; int buflen = 0, len, r; + const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } }; + const struct ether_addr *expected_chaddr = NULL; + uint8_t expected_hlen = 0; assert(s); assert(client); @@ -1285,25 +1388,41 @@ static int client_receive_message_udp(sd_event_source *s, int fd, return 0; } - if (be32toh(message->xid) != client->xid) { - log_dhcp_client(client, "received xid (%u) does not match " - "expected (%u): ignoring", - be32toh(message->xid), client->xid); + if (message->htype != client->arp_type) { + log_dhcp_client(client, "packet type does not match client type"); return 0; } - if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) { - log_dhcp_client(client, "not an ethernet packet"); + if (client->arp_type == ARPHRD_ETHER) { + expected_hlen = ETH_ALEN; + expected_chaddr = (const struct ether_addr *) &client->mac_addr; + } else { + /* Non-ethernet links expect zero chaddr */ + expected_hlen = 0; + expected_chaddr = &zero_mac; + } + + if (message->hlen != expected_hlen) { + log_dhcp_client(client, "unexpected packet hlen %d", message->hlen); return 0; } - if (memcmp(&message->chaddr[0], &client->client_id.mac_addr, - ETH_ALEN)) { + if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { log_dhcp_client(client, "received chaddr does not match " "expected: ignoring"); return 0; } + if (client->state != DHCP_STATE_BOUND && + be32toh(message->xid) != client->xid) { + /* in BOUND state, we may receive FORCERENEW with xid set by server, + so ignore the xid in this case */ + log_dhcp_client(client, "received xid (%u) does not match " + "expected (%u): ignoring", + be32toh(message->xid), client->xid); + return 0; + } + return client_handle_message(client, message, len); } @@ -1383,9 +1502,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { r = client_start(client); if (r >= 0) - log_dhcp_client(client, "STARTED on ifindex %u with address %s", - client->index, - ether_ntoa(&client->client_id.mac_addr)); + log_dhcp_client(client, "STARTED on ifindex %u", client->index); return r; } @@ -1479,6 +1596,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) { client->index = -1; client->fd = -1; client->attempt = 1; + client->mtu = DHCP_DEFAULT_MIN_SIZE; client->req_opts_size = ELEMENTSOF(default_req_opts);