X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdhcp-client.c;h=460fedf90f8f12812081efab1b6ae40217e7e9f4;hb=ca2e44b48d767d6c7c96df42b1a36d0dcf3309dc;hp=2c7e4876853e1f20cc2eaf34e7e0670cfa21ab9b;hpb=72984bb5e98c9f3aa38b023f6fa962c60a349bcc;p=sliver-openvswitch.git diff --git a/lib/dhcp-client.c b/lib/dhcp-client.c index 2c7e48768..460fedf90 100644 --- a/lib/dhcp-client.c +++ b/lib/dhcp-client.c @@ -43,14 +43,14 @@ #include #include #include -#include "buffer.h" #include "csum.h" #include "dhcp.h" #include "dynamic-string.h" #include "flow.h" #include "netdev.h" -#include "ofp-print.h" +#include "ofpbuf.h" #include "poll-loop.h" +#include "sat-math.h" #include "timeval.h" #define THIS_MODULE VLM_dhcp_client @@ -72,6 +72,8 @@ enum dhclient_state { #undef DHCLIENT_STATE }; +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); + static const char * state_name(enum dhclient_state state) { @@ -101,6 +103,7 @@ struct dhclient { bool changed; unsigned int retransmit, delay; /* Used by send_reliably(). */ + unsigned int max_timeout; unsigned int init_delay; /* Used by S_INIT. */ @@ -140,9 +143,6 @@ static unsigned int calc_t2(unsigned int lease); static unsigned int calc_t1(unsigned int lease, unsigned int t2); static unsigned int clamp(unsigned int x, unsigned int min, unsigned int max); -static unsigned int sat_add(unsigned int x, unsigned int y); -static unsigned int sat_sub(unsigned int x, unsigned int y); -static unsigned int sat_mul(unsigned int x, unsigned int y); /* Creates a new DHCP client to configure the network device 'netdev_name' * (e.g. "eth0"). @@ -200,6 +200,7 @@ dhclient_create(const char *netdev_name, cli->ipaddr = 0; cli->server_ip = 0; cli->retransmit = cli->delay = 0; + cli->max_timeout = 64; cli->min_timeout = 1; ds_init(&cli->s); cli->changed = true; @@ -207,6 +208,36 @@ dhclient_create(const char *netdev_name, return 0; } +/* Sets the maximum amount of timeout that 'cli' will wait for a reply from + * the DHCP server before retransmitting, in seconds, to 'max_timeout'. The + * default is 64 seconds. */ +void +dhclient_set_max_timeout(struct dhclient *cli, unsigned int max_timeout) +{ + cli->max_timeout = MAX(2, max_timeout); +} + +/* Destroys 'cli' and frees all related resources. */ +void +dhclient_destroy(struct dhclient *cli) +{ + if (cli) { + dhcp_msg_uninit(cli->binding); + free(cli->binding); + netdev_close(cli->netdev); + ds_destroy(&cli->s); + free(cli); + } +} + +/* Returns the network device in use by 'cli'. The caller must not destroy + * the returned device. */ +struct netdev * +dhclient_get_netdev(struct dhclient *cli) +{ + return cli->netdev; +} + /* Forces 'cli' into a (re)initialization state, in which no address is bound * but the client is advertising to obtain one. If 'requested_ip' is nonzero, * then the client will attempt to re-bind to that IP address; otherwise, it @@ -305,6 +336,29 @@ dhclient_changed(struct dhclient *cli) return changed; } +/* Returns 'cli''s current state, as a string. The caller must not modify or + * free the string. */ +const char * +dhclient_get_state(const struct dhclient *cli) +{ + return state_name(cli->state); +} + +/* Returns the number of seconds spent so far in 'cli''s current state. */ +unsigned int +dhclient_get_state_elapsed(const struct dhclient *cli) +{ + return elapsed_in_this_state(cli); +} + +/* If 'cli' is bound, returns the number of seconds remaining in its lease; + * otherwise, returns 0. */ +unsigned int +dhclient_get_lease_remaining(const struct dhclient *cli) +{ + return dhclient_is_bound(cli) ? cli->lease_expiration - time_now() : 0; +} + /* If 'cli' is bound to an IP address, returns that IP address; otherwise, * returns 0. */ uint32_t @@ -520,14 +574,15 @@ dhcp_receive(struct dhclient *cli, unsigned int msgs, struct dhcp_msg *msg) { while (do_receive_msg(cli, msg)) { if (msg->type < 0 || msg->type > 31 || !((1u << msg->type) & msgs)) { - VLOG_DBG("received unexpected %s in %s state: %s", - dhcp_type_name(msg->type), state_name(cli->state), - dhcp_msg_to_string(msg, false, &cli->s)); + VLOG_DBG_RL(&rl, "received unexpected %s in %s state: %s", + dhcp_type_name(msg->type), state_name(cli->state), + dhcp_msg_to_string(msg, false, &cli->s)); } else if (msg->xid != cli->xid) { - VLOG_DBG("ignoring %s with xid != %08"PRIx32" in %s state: %s", - dhcp_type_name(msg->type), msg->xid, - state_name(cli->state), - dhcp_msg_to_string(msg, false, &cli->s)); + VLOG_DBG_RL(&rl, + "ignoring %s with xid != %08"PRIx32" in %s state: %s", + dhcp_type_name(msg->type), msg->xid, + state_name(cli->state), + dhcp_msg_to_string(msg, false, &cli->s)); } else { return true; } @@ -541,18 +596,18 @@ validate_offered_options(struct dhclient *cli, const struct dhcp_msg *msg) { uint32_t lease, netmask; if (!dhcp_msg_get_secs(msg, DHCP_CODE_LEASE_TIME, 0, &lease)) { - VLOG_WARN("%s lacks lease time: %s", dhcp_type_name(msg->type), - dhcp_msg_to_string(msg, false, &cli->s)); + VLOG_WARN_RL(&rl, "%s lacks lease time: %s", dhcp_type_name(msg->type), + dhcp_msg_to_string(msg, false, &cli->s)); } else if (!dhcp_msg_get_ip(msg, DHCP_CODE_SUBNET_MASK, 0, &netmask)) { - VLOG_WARN("%s lacks netmask: %s", dhcp_type_name(msg->type), - dhcp_msg_to_string(msg, false, &cli->s)); + VLOG_WARN_RL(&rl, "%s lacks netmask: %s", dhcp_type_name(msg->type), + dhcp_msg_to_string(msg, false, &cli->s)); } else if (lease < MIN_ACCEPTABLE_LEASE) { - VLOG_WARN("Ignoring %s with %"PRIu32"-second lease time: %s", - dhcp_type_name(msg->type), lease, - dhcp_msg_to_string(msg, false, &cli->s)); + VLOG_WARN_RL(&rl, "Ignoring %s with %"PRIu32"-second lease time: %s", + dhcp_type_name(msg->type), lease, + dhcp_msg_to_string(msg, false, &cli->s)); } else if (cli->validate_offer && !cli->validate_offer(msg, cli->aux)) { - VLOG_DBG("client validation hook refused offer: %s", - dhcp_msg_to_string(msg, false, &cli->s)); + VLOG_DBG_RL(&rl, "client validation hook refused offer: %s", + dhcp_msg_to_string(msg, false, &cli->s)); } else { return true; } @@ -575,13 +630,13 @@ dhclient_run_SELECTING(struct dhclient *cli) } if (!dhcp_msg_get_ip(&msg, DHCP_CODE_SERVER_IDENTIFIER, 0, &cli->server_ip)) { - VLOG_WARN("DHCPOFFER lacks server identifier: %s", - dhcp_msg_to_string(&msg, false, &cli->s)); + VLOG_WARN_RL(&rl, "DHCPOFFER lacks server identifier: %s", + dhcp_msg_to_string(&msg, false, &cli->s)); continue; } - VLOG_DBG("accepting DHCPOFFER: %s", - dhcp_msg_to_string(&msg, false, &cli->s)); + VLOG_DBG_RL(&rl, "accepting DHCPOFFER: %s", + dhcp_msg_to_string(&msg, false, &cli->s)); cli->ipaddr = msg.yiaddr; state_transition(cli, S_REQUESTING); break; @@ -756,7 +811,13 @@ void dhclient_wait(struct dhclient *cli) { if (cli->min_timeout != UINT_MAX) { - poll_timer_wait(sat_mul(cli->min_timeout, 1000)); + time_t now = time_now(); + unsigned int wake = sat_add(cli->state_entered, cli->min_timeout); + if (wake <= now) { + poll_immediate_wake(); + } else { + poll_timer_wait(sat_mul(sat_sub(wake, now), 1000)); + } } /* Reset timeout to 1 second. This will have no effect ordinarily, because * dhclient_run() will typically set it back to a higher value. If, @@ -817,7 +878,7 @@ send_reliably(struct dhclient *cli, cli->modify_request(&msg, cli->aux); } do_send_msg(cli, &msg); - cli->delay = MIN(64, MAX(4, cli->delay * 2)); + cli->delay = MIN(cli->max_timeout, MAX(4, cli->delay * 2)); cli->retransmit += fuzz(cli->delay, 1); timeout(cli, cli->retransmit); dhcp_msg_uninit(&msg); @@ -852,16 +913,16 @@ timeout(struct dhclient *cli, unsigned int secs) static bool do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg) { - struct buffer b; + struct ofpbuf b; - buffer_init(&b, netdev_get_mtu(cli->netdev) + VLAN_ETH_HEADER_LEN); + ofpbuf_init(&b, netdev_get_mtu(cli->netdev) + VLAN_ETH_HEADER_LEN); for (; cli->received < 50; cli->received++) { const struct ip_header *ip; const struct dhcp_header *dhcp; struct flow flow; int error; - buffer_clear(&b); + ofpbuf_clear(&b); error = netdev_recv(cli->netdev, &b); if (error) { goto drained; @@ -880,42 +941,47 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg) ip = b.l3; if (IP_IS_FRAGMENT(ip->ip_frag_off)) { /* We don't do reassembly. */ - VLOG_WARN("ignoring fragmented DHCP datagram"); + VLOG_WARN_RL(&rl, "ignoring fragmented DHCP datagram"); continue; } dhcp = b.l7; if (!dhcp) { - VLOG_WARN("ignoring DHCP datagram with missing payload"); + VLOG_WARN_RL(&rl, "ignoring DHCP datagram with missing payload"); continue; } - buffer_pull(&b, b.l7 - b.data); + ofpbuf_pull(&b, (char *)b.l7 - (char*)b.data); error = dhcp_parse(msg, &b); if (!error) { - VLOG_DBG("received %s", dhcp_msg_to_string(msg, false, &cli->s)); - buffer_uninit(&b); + if (VLOG_IS_DBG_ENABLED()) { + VLOG_DBG_RL(&rl, "received %s", + dhcp_msg_to_string(msg, false, &cli->s)); + } else { + VLOG_WARN_RL(&rl, "received %s", dhcp_type_name(msg->type)); + } + ofpbuf_uninit(&b); return true; } } netdev_drain(cli->netdev); drained: - buffer_uninit(&b); + ofpbuf_uninit(&b); return false; } static void do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg) { - struct buffer b; + struct ofpbuf b; struct eth_header eh; struct ip_header nh; struct udp_header th; uint32_t udp_csum; int error; - buffer_init(&b, ETH_TOTAL_MAX); - buffer_reserve(&b, ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN); + ofpbuf_init(&b, ETH_TOTAL_MAX); + ofpbuf_reserve(&b, ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN); dhcp_assemble(msg, &b); @@ -959,16 +1025,20 @@ do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg) udp_csum = csum_continue(udp_csum, &th, sizeof th); th.udp_csum = csum_finish(csum_continue(udp_csum, b.data, b.size)); - buffer_push(&b, &th, sizeof th); - buffer_push(&b, &nh, sizeof nh); - buffer_push(&b, &eh, sizeof eh); + ofpbuf_push(&b, &th, sizeof th); + ofpbuf_push(&b, &nh, sizeof nh); + ofpbuf_push(&b, &eh, sizeof eh); /* Don't try to send the frame if it's too long for an Ethernet frame. We * disregard the network device's actual MTU because we don't want the * frame to have to be discarded or fragmented if it travels over a regular * Ethernet at some point. 1500 bytes should be enough for anyone. */ if (b.size <= ETH_TOTAL_MAX) { - VLOG_DBG("sending %s", dhcp_msg_to_string(msg, false, &cli->s)); + if (VLOG_IS_DBG_ENABLED()) { + VLOG_DBG("sending %s", dhcp_msg_to_string(msg, false, &cli->s)); + } else { + VLOG_WARN("sending %s", dhcp_type_name(msg->type)); + } error = netdev_send(cli->netdev, &b); if (error) { VLOG_ERR("send failed on %s: %s", @@ -978,7 +1048,7 @@ do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg) VLOG_ERR("cannot send %zu-byte Ethernet frame", b.size); } - buffer_uninit(&b); + ofpbuf_uninit(&b); } static unsigned int @@ -990,25 +1060,6 @@ fuzz(unsigned int x, int max_fuzz) return fuzz >= 0 ? (y >= x ? y : UINT_MAX) : (y <= x ? y : 0); } -static unsigned int -sat_add(unsigned int x, unsigned int y) -{ - return x + y >= x ? x + y : UINT_MAX; -} - -static unsigned int -sat_sub(unsigned int x, unsigned int y) -{ - return x >= y ? x - y : 0; -} - -static unsigned int -sat_mul(unsigned int x, unsigned int y) -{ - assert(y); - return x <= UINT_MAX / y ? x * y : UINT_MAX; -} - static unsigned int clamp(unsigned int x, unsigned int min, unsigned int max) {