diff --git a/docs/config.md b/docs/config.md index a6350254..d51af007 100644 --- a/docs/config.md +++ b/docs/config.md @@ -455,9 +455,11 @@ This section describes all attributes of the `dhcp` hierarchy. Attribute | Description | Default --------- | ----------- | ------- -`enable` | This option allows to enable or disable DHCP | true - -**WARNING**: DHCP (IPv4) is currently not supported! +`enable` | This option allows to enable or disable DHCP | false +`broadcast` | DHCP broadcast flag | false +`timeout` | DHCP timeout in seconds | 5 +`tos` | IPv4 TOS for all DHCP control traffic | 0 +`vlan-priority` | VLAN PBIT for all DHCP control traffic | 0 ## DHCPv6 diff --git a/src/bbl.c b/src/bbl.c index 531a7a6e..5f94bf8c 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -520,7 +520,7 @@ bbl_ctrl_job (timer_s *timer) /* Start IPoE session by sending DHCP discovery if enabled. */ session->dhcp_state = BBL_DHCP_SELECTING; session->dhcp_xid = rand(); - session->send_requests |= BBL_SEND_DHCPREQUEST; + session->send_requests |= BBL_SEND_DHCP_REQUEST; } else if (session->ip_address && session->peer_ip_address) { /* Start IPoE session by sending ARP request if local and * remote IP addresses are already provided. */ diff --git a/src/bbl.h b/src/bbl.h index a620091d..dcc941fc 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -76,7 +76,7 @@ #define BBL_SEND_SESSION_IPV6PD 0x00008000 #define BBL_SEND_ARP_REQUEST 0x00010000 #define BBL_SEND_ARP_REPLY 0x00020000 -#define BBL_SEND_DHCPREQUEST 0x00040000 +#define BBL_SEND_DHCP_REQUEST 0x00040000 #define BBL_SEND_ICMPV6_REPLY 0x00080000 #define BBL_SEND_ICMPV6_NS 0x00100000 #define BBL_SEND_ICMPV6_NA 0x00200000 @@ -585,7 +585,7 @@ typedef struct bbl_ctx_ /* DHCP */ bool dhcp_enable; uint16_t dhcp_timeout; - uint16_t dhcp_retry; + uint8_t dhcp_tos; uint8_t dhcp_vlan_priority; bool dhcp_broadcast; @@ -670,7 +670,7 @@ typedef enum { BBL_DHCP_REQUESTING = 2, BBL_DHCP_BOUND = 3, BBL_DHCP_RENEWING = 4, - BBL_DHCP_DHCPRELEASE = 5, + BBL_DHCP_RELEASE = 5, BBL_DHCP_MAX } __attribute__ ((__packed__)) dhcp_state_t; @@ -833,8 +833,10 @@ typedef struct bbl_session_ uint32_t dhcp_address; uint32_t dhcp_lease_time; uint32_t dhcp_t1; - uint32_t dhcp_t2; + uint32_t dhcp_t2; + uint32_t dhcp_server; uint32_t dhcp_server_identifier; + uint8_t dhcp_server_mac[ETH_ADDR_LEN]; struct timespec dhcp_lease_timestamp; struct timespec dhcp_request_timestamp; char *dhcp_client_identifier; @@ -967,6 +969,12 @@ typedef struct bbl_session_ uint32_t dhcp_tx; uint32_t dhcp_rx; + uint32_t dhcp_tx_discover; + uint32_t dhcp_rx_offer; + uint32_t dhcp_tx_request; + uint32_t dhcp_rx_ack; + uint32_t dhcp_rx_nak; + uint32_t dhcp_tx_release; uint64_t access_ipv4_rx; uint64_t access_ipv4_tx; diff --git a/src/bbl_config.c b/src/bbl_config.c index 5c5953f6..6715f5aa 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -689,9 +689,9 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { if (json_is_number(value)) { ctx->config.dhcp_timeout = json_number_value(value); } - value = json_object_get(section, "retry"); + value = json_object_get(section, "tos"); if (json_is_number(value)) { - ctx->config.dhcp_retry = json_number_value(value); + ctx->config.dhcp_tos = json_number_value(value); } value = json_object_get(section, "vlan-priority"); if (json_is_number(value)) { @@ -1130,7 +1130,6 @@ bbl_config_init_defaults (bbl_ctx_s *ctx) { ctx->config.ip6cp_conf_request_retry = 10; ctx->config.dhcp_enable = false; ctx->config.dhcp_timeout = 5; - ctx->config.dhcp_retry = 10; ctx->config.dhcpv6_enable = true; ctx->config.dhcpv6_rapid_commit = true; ctx->config.igmp_autostart = true; diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 18d23362..597b1196 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -73,7 +73,7 @@ dhcp_state_string(uint32_t state) { case BBL_DHCP_REQUESTING: return "Requesting"; case BBL_DHCP_BOUND: return "Bound"; case BBL_DHCP_RENEWING: return "Renewing"; - case BBL_DHCP_DHCPRELEASE: return "Releasing"; + case BBL_DHCP_RELEASE: return "Releasing"; default: return "N/A"; } } @@ -555,7 +555,7 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* argum if(seconds <= session->dhcp_lease_time) lease_expire = session->dhcp_lease_time - seconds; if(seconds <= session->dhcp_t1) lease_expire_t1 = session->dhcp_t1 - seconds; if(seconds <= session->dhcp_t2) lease_expire_t2 = session->dhcp_t2 - seconds; - root = json_pack("{ss si s{ss si ss ss si si ss ss* ss* ss* ss* si si si si ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* si si si so*}}", + root = json_pack("{ss si s{ss si ss ss si si ss ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* si si si si si si si si si si ss* ss* si si si so*}}", "status", "ok", "code", 200, "session-information", @@ -568,12 +568,6 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* argum "mac", format_mac_address(session->client_mac), "agent-circuit-id", session->agent_circuit_id, "agent-remote-id", session->agent_remote_id, - "dhcp-state", dhcp_state_string(session->dhcp_state), - "dhcp-server", format_ipv4_address(&session->dhcp_server_identifier), - "dhcp-lease-time", session->dhcp_lease_time, - "dhcp-lease-expire", lease_expire, - "dhcp-lease-expire-t1", lease_expire_t1, - "dhcp-lease-expire-t2", lease_expire_t2, "ipv4-address", ipv4, "ipv4-netmask", ipv4_netmask, "ipv4-gateway", ipv4_gw, @@ -583,6 +577,18 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* argum "ipv6-delegated-prefix", ipv6pd, "ipv6-dns1", ipv6_dns1, "ipv6-dns2", ipv6_dns2, + "dhcp-state", dhcp_state_string(session->dhcp_state), + "dhcp-server", format_ipv4_address(&session->dhcp_server_identifier), + "dhcp-lease-time", session->dhcp_lease_time, + "dhcp-lease-expire", lease_expire, + "dhcp-lease-expire-t1", lease_expire_t1, + "dhcp-lease-expire-t2", lease_expire_t2, + "dhcp-tx-discover", session->stats.dhcp_tx_discover, + "dhcp-tx-request", session->stats.dhcp_tx_request, + "dhcp-tx-release", session->stats.dhcp_tx_release, + "dhcp-rx-offer", session->stats.dhcp_rx_offer, + "dhcp-rx-ack", session->stats.dhcp_rx_ack, + "dhcp-rx-nak", session->stats.dhcp_rx_nak, "dhcpv6-dns1", dhcpv6_dns1, "dhcpv6-dns2", dhcpv6_dns2, "tx-packets", session->stats.packets_tx, diff --git a/src/bbl_dhcp.c b/src/bbl_dhcp.c index da8f4da8..19d69a0a 100644 --- a/src/bbl_dhcp.c +++ b/src/bbl_dhcp.c @@ -38,7 +38,9 @@ bbl_dhcp_restart(bbl_session_s *session) { session->dhcp_lease_time = 0; session->dhcp_t1 = 0; session->dhcp_t2 = 0; + session->dhcp_server = 0; session->dhcp_server_identifier = 0; + memset(&session->dhcp_server_mac, 0xff, ETH_ADDR_LEN); // init with broadcast MAC session->dhcp_lease_timestamp.tv_sec = 0; session->dhcp_lease_timestamp.tv_nsec = 0; session->dhcp_request_timestamp.tv_sec = 0; @@ -51,7 +53,7 @@ bbl_dhcp_restart(bbl_session_s *session) { free(session->dhcp_domain_name); session->dhcp_domain_name = NULL; } - session->send_requests |= BBL_SEND_DHCPREQUEST; + session->send_requests |= BBL_SEND_DHCP_REQUEST; bbl_session_tx_qnode_insert(session); /* Reset session IP configuration */ @@ -82,7 +84,7 @@ bbl_dhcp_t1(timer_s *timer) { session->dhcp_request_timestamp.tv_sec = 0; session->dhcp_request_timestamp.tv_nsec = 0; session->dhcp_state = BBL_DHCP_RENEWING; - session->send_requests = BBL_SEND_DHCPREQUEST; + session->send_requests = BBL_SEND_DHCP_REQUEST; bbl_session_tx_qnode_insert(session); } } @@ -113,21 +115,41 @@ bbl_dhcp_rx(bbl_ethernet_header_t *eth, bbl_dhcp_t *dhcp, bbl_session_s *session return; } + switch(dhcp->type) { + case DHCP_MESSAGE_OFFER: + session->stats.dhcp_rx_offer++; + LOG(DHCP, "DHCP (ID: %u) DHCP-Offer received\n", session->session_id); + break; + case DHCP_MESSAGE_ACK: + session->stats.dhcp_rx_ack++; + LOG(DHCP, "DHCP (ID: %u) DHCP-ACK received\n", session->session_id); + break; + case DHCP_MESSAGE_NAK: + session->stats.dhcp_rx_nak++; + LOG(DHCP, "DHCP (ID: %u) DHCP-NAK received\n", session->session_id); + break; + default: + /* Not expected ... */ + return; + } + switch(session->dhcp_state) { case BBL_DHCP_SELECTING: if(dhcp->type == DHCP_MESSAGE_OFFER) { session->dhcp_state = BBL_DHCP_REQUESTING; session->dhcp_address = dhcp->header->yiaddr; + session->dhcp_server = dhcp->header->siaddr; session->dhcp_server_identifier = dhcp->server_identifier; + memcpy(session->dhcp_server_mac, eth->src, ETH_ADDR_LEN); session->dhcp_lease_time = dhcp->lease_time; session->dhcp_lease_timestamp.tv_sec = eth->timestamp.tv_sec; session->dhcp_lease_timestamp.tv_nsec = eth->timestamp.tv_nsec; if(!(session->dhcp_address && session->dhcp_server_identifier && session->dhcp_lease_time)) { - LOG(ERROR, "DHCP (ID: %u) Invalid DHCP-OFFER!\n", session->session_id); + LOG(ERROR, "DHCP (ID: %u) Invalid DHCP-Offer!\n", session->session_id); return bbl_dhcp_restart(session); } timer_add(&ctx->timer_root, &session->timer_dhcp_t2, "DHCP T2", session->dhcp_lease_time, 0, session, &bbl_dhcp_t2); - session->send_requests |= BBL_SEND_DHCPREQUEST; + session->send_requests |= BBL_SEND_DHCP_REQUEST; bbl_session_tx_qnode_insert(session); } break; @@ -135,6 +157,7 @@ bbl_dhcp_rx(bbl_ethernet_header_t *eth, bbl_dhcp_t *dhcp, bbl_session_s *session if(dhcp->type == DHCP_MESSAGE_ACK) { session->dhcp_state = BBL_DHCP_BOUND; session->dhcp_address = dhcp->header->yiaddr; + session->dhcp_server = dhcp->header->siaddr; session->dhcp_server_identifier = dhcp->server_identifier; session->dhcp_lease_time = dhcp->lease_time; session->dhcp_lease_timestamp.tv_sec = eth->timestamp.tv_sec; @@ -193,12 +216,13 @@ bbl_dhcp_rx(bbl_ethernet_header_t *eth, bbl_dhcp_t *dhcp, bbl_session_s *session if(dhcp->type == DHCP_MESSAGE_ACK) { session->dhcp_state = BBL_DHCP_BOUND; session->dhcp_address = dhcp->header->yiaddr; + session->dhcp_server = dhcp->header->siaddr; session->dhcp_server_identifier = dhcp->server_identifier; session->dhcp_lease_time = dhcp->lease_time; session->dhcp_lease_timestamp.tv_sec = eth->timestamp.tv_sec; session->dhcp_lease_timestamp.tv_nsec = eth->timestamp.tv_nsec; if(!(session->dhcp_address && session->dhcp_server_identifier && session->dhcp_lease_time)) { - LOG(ERROR, "DHCP (ID: %u) Restart because of invalid DHCP-ACK!\n", session->session_id); + LOG(ERROR, "DHCP (ID: %u) Invalid DHCP-ACK!\n", session->session_id); return bbl_dhcp_restart(session); } session->dhcp_t1 = 0.5 * session->dhcp_lease_time; if(!session->dhcp_t1) session->dhcp_t1 = 1; diff --git a/src/bbl_session.c b/src/bbl_session.c index 29631813..ae7a2496 100644 --- a/src/bbl_session.c +++ b/src/bbl_session.c @@ -273,7 +273,17 @@ bbl_session_clear(bbl_ctx_s *ctx, bbl_session_s *session) break; } } else { - bbl_session_update_state(ctx, session, BBL_TERMINATED); + if(session->dhcp_state > BBL_DHCP_SELECTING) { + bbl_session_update_state(ctx, session, BBL_TERMINATING); + session->dhcp_state = BBL_DHCP_RELEASE; + session->dhcp_xid = rand(); + session->dhcp_request_timestamp.tv_sec = 0; + session->dhcp_request_timestamp.tv_nsec = 0; + session->send_requests |= BBL_SEND_DHCP_REQUEST; + bbl_session_tx_qnode_insert(session); + } else { + bbl_session_update_state(ctx, session, BBL_TERMINATED); + } } } @@ -360,6 +370,7 @@ bbl_sessions_init(bbl_ctx_s *ctx) return false; } memset(&session->server_mac, 0xff, ETH_ADDR_LEN); // init with broadcast MAC + memset(&session->dhcp_server_mac, 0xff, ETH_ADDR_LEN); // init with broadcast MAC session->session_id = i; // BNG Blaster internal session identifier session->access_type = access_config->access_type; session->vlan_key.ifindex = access_config->access_if->ifindex; diff --git a/src/bbl_tx.c b/src/bbl_tx.c index aa8beeac..b7e60979 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -1201,7 +1201,7 @@ bbl_dhcp_timeout (timer_s *timer) return; } - session->send_requests = BBL_SEND_DHCPREQUEST; + session->send_requests = BBL_SEND_DHCP_REQUEST; bbl_session_tx_qnode_insert(session); } @@ -1228,19 +1228,20 @@ bbl_encode_packet_dhcp (bbl_session_s *session) { dhcp.header = &header; + eth.dst = session->dhcp_server_mac; eth.src = session->client_mac; - eth.dst = (uint8_t*)broadcast_mac; eth.vlan_outer = session->vlan_key.outer_vlan_id; eth.vlan_inner = session->vlan_key.inner_vlan_id; eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = ctx->config.pppoe_vlan_priority; + eth.vlan_outer_priority = ctx->config.dhcp_vlan_priority; eth.vlan_inner_priority = eth.vlan_outer_priority; eth.type = ETH_TYPE_IPV4; eth.next = &ipv4; ipv4.src = session->ip_address; - ipv4.dst = IPV4_BROADCAST; + ipv4.dst = session->dhcp_server; ipv4.ttl = 255; + ipv4.tos = ctx->config.dhcp_tos; ipv4.protocol = PROTOCOL_IPV4_UDP; ipv4.next = &udp; udp.src = DHCP_UDP_CLIENT; @@ -1253,8 +1254,10 @@ bbl_encode_packet_dhcp (bbl_session_s *session) { header.htype = 1; /* fixed set to ethernet */ header.hlen = 6; header.xid = session->dhcp_xid; - if(ctx->config.dhcp_broadcast) { + if(ctx->config.dhcp_broadcast && session->dhcp_state != BBL_DHCP_RELEASE) { header.flags = htobe16(1 << 15); + eth.dst = (uint8_t*)broadcast_mac; + ipv4.dst = IPV4_BROADCAST; } header.ciaddr = session->ip_address; memcpy(header.chaddr, session->client_mac, ETH_ADDR_LEN); @@ -1270,7 +1273,7 @@ bbl_encode_packet_dhcp (bbl_session_s *session) { } /* Option 82 ... */ - if(session->agent_circuit_id || session->agent_remote_id) { + if((session->agent_circuit_id || session->agent_remote_id) && session->dhcp_state != BBL_DHCP_RELEASE) { access_line.aci = session->agent_circuit_id; access_line.ari = session->agent_remote_id; dhcp.access_line = &access_line; @@ -1279,6 +1282,10 @@ bbl_encode_packet_dhcp (bbl_session_s *session) { switch(session->dhcp_state) { case BBL_DHCP_SELECTING: dhcp.type = DHCP_MESSAGE_DISCOVER; + session->stats.dhcp_tx_discover++; + LOG(DHCP, "DHCP (ID: %u) DHCP-Discover send\n", session->session_id); + eth.dst = (uint8_t*)broadcast_mac; + ipv4.dst = IPV4_BROADCAST; dhcp.parameter_request_list = true; dhcp.option_netmask = true; dhcp.option_dns1 = true; @@ -1293,6 +1300,8 @@ bbl_encode_packet_dhcp (bbl_session_s *session) { break; case BBL_DHCP_REQUESTING: dhcp.type = DHCP_MESSAGE_REQUEST; + session->stats.dhcp_tx_request++; + LOG(DHCP, "DHCP (ID: %u) DHCP-Request send\n", session->session_id); dhcp.option_address = true; dhcp.address = session->dhcp_address; dhcp.option_server_identifier = true; @@ -1307,16 +1316,24 @@ bbl_encode_packet_dhcp (bbl_session_s *session) { break; case BBL_DHCP_RENEWING: dhcp.type = DHCP_MESSAGE_REQUEST; + session->stats.dhcp_tx_request++; + LOG(DHCP, "DHCP (ID: %u) DHCP-Request send\n", session->session_id); break; - case BBL_DHCP_DHCPRELEASE: + case BBL_DHCP_RELEASE: dhcp.type = DHCP_MESSAGE_RELEASE; - header.flags = 0; + session->stats.dhcp_tx_release++; + LOG(DHCP, "DHCP (ID: %u) DHCP-Release send\n", session->session_id); + dhcp.option_server_identifier = true; + dhcp.server_identifier = session->dhcp_server_identifier; + if(session->session_state == BBL_TERMINATING) { + bbl_session_update_state(ctx, session, BBL_TERMINATED); + } break; default: return IGNORED; } - timer_add(&ctx->timer_root, &session->timer_dhcp_retry, "DHCP timeout", 5, 0, session, &bbl_dhcp_timeout); + timer_add(&ctx->timer_root, &session->timer_dhcp_retry, "DHCP timeout", ctx->config.dhcp_timeout, 0, session, &bbl_dhcp_timeout); interface->stats.dhcp_tx++; return encode_ethernet(session->write_buf, &session->write_idx, ð); @@ -1456,9 +1473,9 @@ bbl_encode_packet (bbl_session_s *session, uint8_t *buf, uint16_t *len, bool *ac } else if (session->send_requests & BBL_SEND_ARP_REPLY) { result = bbl_encode_packet_arp_reply(session); session->send_requests &= ~BBL_SEND_ARP_REPLY; - } else if (session->send_requests & BBL_SEND_DHCPREQUEST) { + } else if (session->send_requests & BBL_SEND_DHCP_REQUEST) { result = bbl_encode_packet_dhcp(session); - session->send_requests &= ~BBL_SEND_DHCPREQUEST; + session->send_requests &= ~BBL_SEND_DHCP_REQUEST; } else { session->send_requests = 0; }