Revert "[dhcpmon] Monitor Mgmt Interface For DHCP Packets (#5317)"

This reverts commit 44d6e03df3.
This commit is contained in:
Ying Xie 2020-09-14 22:03:55 +00:00
parent 44d6e03df3
commit 6c4914b62c
8 changed files with 163 additions and 452 deletions

View File

@ -105,11 +105,6 @@ command=/usr/sbin/dhcpmon -id {{ vlan_name }}
{% for (name, prefix) in PORTCHANNEL_INTERFACE %} {% for (name, prefix) in PORTCHANNEL_INTERFACE %}
{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} {% if prefix | ipv4 %} -iu {{ name }}{% endif -%}
{% endfor %} {% endfor %}
{% if MGMT_INTERFACE %}
{% for (name, prefix) in MGMT_INTERFACE|pfx_filter %}
{% if prefix | ipv4 %} -im {{ name }}{% endif %}
{% endfor %}
{% endif %}
priority=4 priority=4
autostart=false autostart=false

View File

@ -8,7 +8,6 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <net/ethernet.h> #include <net/ethernet.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <netinet/udp.h> #include <netinet/udp.h>
@ -23,9 +22,6 @@
#include "dhcp_device.h" #include "dhcp_device.h"
/** Counter print width */
#define DHCP_COUNTER_WIDTH 10
/** Start of Ether header of a captured frame */ /** Start of Ether header of a captured frame */
#define ETHER_START_OFFSET 0 #define ETHER_START_OFFSET 0
/** Start of IP header of a captured frame */ /** Start of IP header of a captured frame */
@ -39,6 +35,21 @@
/** Offset of DHCP GIADDR */ /** Offset of DHCP GIADDR */
#define DHCP_GIADDR_OFFSET 24 #define DHCP_GIADDR_OFFSET 24
/**
* DHCP message types
**/
typedef enum
{
DHCP_MESSAGE_TYPE_DISCOVER = 1,
DHCP_MESSAGE_TYPE_OFFER = 2,
DHCP_MESSAGE_TYPE_REQUEST = 3,
DHCP_MESSAGE_TYPE_DECLINE = 4,
DHCP_MESSAGE_TYPE_ACK = 5,
DHCP_MESSAGE_TYPE_NAK = 6,
DHCP_MESSAGE_TYPE_RELEASE = 7,
DHCP_MESSAGE_TYPE_INFORM = 8
} dhcp_message_type;
#define OP_LDHA (BPF_LD | BPF_H | BPF_ABS) /** bpf ldh Abs */ #define OP_LDHA (BPF_LD | BPF_H | BPF_ABS) /** bpf ldh Abs */
#define OP_LDHI (BPF_LD | BPF_H | BPF_IND) /** bpf ldh Ind */ #define OP_LDHI (BPF_LD | BPF_H | BPF_IND) /** bpf ldh Ind */
#define OP_LDB (BPF_LD | BPF_B | BPF_ABS) /** bpf ldb Abs*/ #define OP_LDB (BPF_LD | BPF_B | BPF_ABS) /** bpf ldb Abs*/
@ -83,21 +94,17 @@ static struct sock_fprog dhcp_sock_bfp = {
.len = sizeof(dhcp_bpf_code) / sizeof(*dhcp_bpf_code), .filter = dhcp_bpf_code .len = sizeof(dhcp_bpf_code) / sizeof(*dhcp_bpf_code), .filter = dhcp_bpf_code
}; };
/** Aggregate device of DHCP interfaces. It contains aggregate counters from /** global aggregate counter for DHCP interfaces */
all interfaces static dhcp_device_counters_t glob_counters[DHCP_DIR_COUNT] = {
*/ [DHCP_RX] = {.discover = 0, .offer = 0, .request = 0, .ack = 0},
static dhcp_device_context_t aggregate_dev = {0}; [DHCP_TX] = {.discover = 0, .offer = 0, .request = 0, .ack = 0},
/** Monitored DHCP message type */
static dhcp_message_type_t monitored_msgs[] = {
DHCP_MESSAGE_TYPE_DISCOVER,
DHCP_MESSAGE_TYPE_OFFER,
DHCP_MESSAGE_TYPE_REQUEST,
DHCP_MESSAGE_TYPE_ACK
}; };
/** Number of monitored DHCP message type */ /** global aggregate counter snapshot for DHCP interfaces */
static uint8_t monitored_msg_sz = sizeof(monitored_msgs) / sizeof(*monitored_msgs); static dhcp_device_counters_t glob_counters_snapshot[DHCP_DIR_COUNT] = {
[DHCP_RX] = {.discover = 0, .offer = 0, .request = 0, .ack = 0},
[DHCP_TX] = {.discover = 0, .offer = 0, .request = 0, .ack = 0},
};
/** /**
* @code handle_dhcp_option_53(context, dhcp_option, dir, iphdr, dhcphdr); * @code handle_dhcp_option_53(context, dhcp_option, dir, iphdr, dhcphdr);
@ -121,30 +128,43 @@ static void handle_dhcp_option_53(dhcp_device_context_t *context,
in_addr_t giaddr; in_addr_t giaddr;
switch (dhcp_option[2]) switch (dhcp_option[2])
{ {
// DHCP messages send by client
case DHCP_MESSAGE_TYPE_DISCOVER: case DHCP_MESSAGE_TYPE_DISCOVER:
case DHCP_MESSAGE_TYPE_REQUEST:
case DHCP_MESSAGE_TYPE_DECLINE:
case DHCP_MESSAGE_TYPE_RELEASE:
case DHCP_MESSAGE_TYPE_INFORM:
giaddr = ntohl(dhcphdr[DHCP_GIADDR_OFFSET] << 24 | dhcphdr[DHCP_GIADDR_OFFSET + 1] << 16 | giaddr = ntohl(dhcphdr[DHCP_GIADDR_OFFSET] << 24 | dhcphdr[DHCP_GIADDR_OFFSET + 1] << 16 |
dhcphdr[DHCP_GIADDR_OFFSET + 2] << 8 | dhcphdr[DHCP_GIADDR_OFFSET + 3]); dhcphdr[DHCP_GIADDR_OFFSET + 2] << 8 | dhcphdr[DHCP_GIADDR_OFFSET + 3]);
context->counters[dir].discover++;
if ((context->vlan_ip == giaddr && context->is_uplink && dir == DHCP_TX) || if ((context->vlan_ip == giaddr && context->is_uplink && dir == DHCP_TX) ||
(!context->is_uplink && dir == DHCP_RX && iphdr->ip_dst.s_addr == INADDR_BROADCAST)) { (!context->is_uplink && dir == DHCP_RX && iphdr->ip_dst.s_addr == INADDR_BROADCAST)) {
context->counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++; glob_counters[dir].discover++;
aggregate_dev.counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
} }
break; break;
// DHCP messages send by server
case DHCP_MESSAGE_TYPE_OFFER: case DHCP_MESSAGE_TYPE_OFFER:
case DHCP_MESSAGE_TYPE_ACK: context->counters[dir].offer++;
case DHCP_MESSAGE_TYPE_NAK:
if ((context->vlan_ip == iphdr->ip_dst.s_addr && context->is_uplink && dir == DHCP_RX) || if ((context->vlan_ip == iphdr->ip_dst.s_addr && context->is_uplink && dir == DHCP_RX) ||
(!context->is_uplink && dir == DHCP_TX)) { (!context->is_uplink && dir == DHCP_TX)) {
context->counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++; glob_counters[dir].offer++;
aggregate_dev.counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
} }
break; break;
case DHCP_MESSAGE_TYPE_REQUEST:
giaddr = ntohl(dhcphdr[DHCP_GIADDR_OFFSET] << 24 | dhcphdr[DHCP_GIADDR_OFFSET + 1] << 16 |
dhcphdr[DHCP_GIADDR_OFFSET + 2] << 8 | dhcphdr[DHCP_GIADDR_OFFSET + 3]);
context->counters[dir].request++;
if ((context->vlan_ip == giaddr && context->is_uplink && dir == DHCP_TX) ||
(!context->is_uplink && dir == DHCP_RX && iphdr->ip_dst.s_addr == INADDR_BROADCAST)) {
glob_counters[dir].request++;
}
break;
case DHCP_MESSAGE_TYPE_ACK:
context->counters[dir].ack++;
if ((context->vlan_ip == iphdr->ip_dst.s_addr && context->is_uplink && dir == DHCP_RX) ||
(!context->is_uplink && dir == DHCP_TX)) {
glob_counters[dir].ack++;
}
break;
case DHCP_MESSAGE_TYPE_DECLINE:
case DHCP_MESSAGE_TYPE_NAK:
case DHCP_MESSAGE_TYPE_RELEASE:
case DHCP_MESSAGE_TYPE_INFORM:
break;
default: default:
syslog(LOG_WARNING, "handle_dhcp_option_53(%s): Unknown DHCP option 53 type %d", context->intf, dhcp_option[2]); syslog(LOG_WARNING, "handle_dhcp_option_53(%s): Unknown DHCP option 53 type %d", context->intf, dhcp_option[2]);
break; break;
@ -221,166 +241,62 @@ static void read_callback(int fd, short event, void *arg)
} }
/** /**
* @code dhcp_device_is_dhcp_inactive(counters); * @code dhcp_device_validate(counters, counters_snapshot);
* *
* @brief Check if there were no DHCP activity * @brief validate current interface counters by comparing aggregate counter with snapshot counters.
*
* @param counters current/snapshot counter
*
* @return true if there were no DHCP activity, false otherwise
*/
static bool dhcp_device_is_dhcp_inactive(uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
uint64_t *rx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_RX];
uint64_t *rx_counter_snapshot = counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX];
bool rv = true;
for (uint8_t i = 0; (i < monitored_msg_sz) && rv; i++) {
rv = rx_counters[monitored_msgs[i]] == rx_counter_snapshot[monitored_msgs[i]];
}
return rv;
}
/**
* @code dhcp_device_is_dhcp_msg_unhealthy(type, counters);
*
* @brief Check if DHCP relay is functioning properly for message of type 'type'.
* For every rx of message 'type', there should be increment of the same message type.
*
* @param type DHCP message type
* @param counters current/snapshot counter
*
* @return true if DHCP message 'type' is transmitted,false otherwise
*/
static bool dhcp_device_is_dhcp_msg_unhealthy(dhcp_message_type_t type,
uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
// check if DHCP message 'type' is being relayed
return ((counters[DHCP_COUNTERS_CURRENT][DHCP_RX][type] > counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX][type]) &&
(counters[DHCP_COUNTERS_CURRENT][DHCP_TX][type] <= counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX][type]) );
}
/**
* @code dhcp_device_check_positive_health(counters, counters_snapshot);
*
* @brief Check if DHCP relay is functioning properly for monitored messages (Discover, Offer, Request, ACK.)
* For every rx of monitored messages, there should be increment of the same message type.
*
* @param counters current/snapshot counter
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_positive_health(uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
bool is_dhcp_unhealthy = false;
for (uint8_t i = 0; (i < monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
is_dhcp_unhealthy = dhcp_device_is_dhcp_msg_unhealthy(monitored_msgs[i], counters);
}
// if we have rx DORA then we should have corresponding tx DORA (DORA being relayed)
if (is_dhcp_unhealthy) {
rv = DHCP_MON_STATUS_UNHEALTHY;
}
return rv;
}
/**
* @code dhcp_device_check_negative_health(counters);
*
* @brief Check that DHCP relayed messages are not being transmitted out of this interface/dev
* using its counters. The interface is negatively healthy if there are not DHCP message
* travelling through it.
* *
* @param counters recent interface counter * @param counters recent interface counter
* @param counters_snapshot snapshot counters * @param counters_snapshot snapshot counters
* *
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE * @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/ */
static dhcp_mon_status_t dhcp_device_check_negative_health(uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT]) static dhcp_mon_status_t dhcp_device_validate(dhcp_device_counters_t *counters,
dhcp_device_counters_t *counters_snapshot)
{ {
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY; dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
uint64_t *tx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_TX]; if ((counters[DHCP_RX].discover == counters_snapshot[DHCP_RX].discover) &&
uint64_t *tx_counter_snapshot = counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX]; (counters[DHCP_RX].offer == counters_snapshot[DHCP_RX].offer ) &&
(counters[DHCP_RX].request == counters_snapshot[DHCP_RX].request ) &&
bool is_dhcp_unhealthy = false; (counters[DHCP_RX].ack == counters_snapshot[DHCP_RX].ack ) ) {
for (uint8_t i = 0; (i < monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
is_dhcp_unhealthy = tx_counters[monitored_msgs[i]] > tx_counter_snapshot[monitored_msgs[i]];
}
// for negative validation, return unhealthy if DHCP packet are being
// transmitted out of the device/interface
if (is_dhcp_unhealthy) {
rv = DHCP_MON_STATUS_UNHEALTHY;
}
return rv;
}
/**
* @code dhcp_device_check_health(check_type, counters, counters_snapshot);
*
* @brief Check that DHCP relay is functioning properly given a check type. Positive check
* indicates for every rx of DHCP message of type 'type', there would increment of
* the corresponding TX of the same message type. While negative check indicates the
* device should not be actively transmitting any DHCP messages. If it does, it is
* considered unhealthy.
*
* @param check_type type of health check
* @param counters current/snapshot counter
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_health(dhcp_mon_check_t check_type,
uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
if (dhcp_device_is_dhcp_inactive(aggregate_dev.counters)) {
rv = DHCP_MON_STATUS_INDETERMINATE; rv = DHCP_MON_STATUS_INDETERMINATE;
} else if (check_type == DHCP_MON_CHECK_POSITIVE) { } else {
rv = dhcp_device_check_positive_health(counters); // if we have rx DORA then we should have corresponding tx DORA (DORA being relayed)
} else if (check_type == DHCP_MON_CHECK_NEGATIVE) { if (((counters[DHCP_RX].discover > counters_snapshot[DHCP_RX].discover) &&
rv = dhcp_device_check_negative_health(counters); (counters[DHCP_TX].discover <= counters_snapshot[DHCP_TX].discover) ) ||
((counters[DHCP_RX].offer > counters_snapshot[DHCP_RX].offer ) &&
(counters[DHCP_TX].offer <= counters_snapshot[DHCP_TX].offer ) ) ||
((counters[DHCP_RX].request > counters_snapshot[DHCP_RX].request ) &&
(counters[DHCP_TX].request <= counters_snapshot[DHCP_TX].request ) ) ||
((counters[DHCP_RX].ack > counters_snapshot[DHCP_RX].ack ) &&
(counters[DHCP_TX].ack <= counters_snapshot[DHCP_TX].ack ) ) ) {
rv = DHCP_MON_STATUS_UNHEALTHY;
}
} }
return rv; return rv;
} }
/** /**
* @code dhcp_print_counters(vlan_intf, counters); * @code dhcp_print_counters(counters);
* *
* @brief prints DHCP counters to sylsog. * @brief prints DHCP counters to sylsog.
* *
* @param counters interface counter * @param counters interface counter
*/ */
static void dhcp_print_counters(const char *vlan_intf, uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT]) static void dhcp_print_counters(dhcp_device_counters_t *counters)
{ {
uint64_t *rx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_RX]; syslog(LOG_NOTICE, "DHCP Discover rx: %lu, tx:%lu, Offer rx: %lu, tx:%lu, Request rx: %lu, tx:%lu, ACK rx: %lu, tx:%lu\n",
uint64_t *tx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_TX]; counters[DHCP_RX].discover, counters[DHCP_TX].discover,
counters[DHCP_RX].offer, counters[DHCP_TX].offer,
syslog(LOG_NOTICE, counters[DHCP_RX].request, counters[DHCP_TX].request,
"[%*s] DHCP Discover rx/tx: %*lu/%*lu, Offer rx/tx: %*lu/%*lu, " counters[DHCP_RX].ack, counters[DHCP_TX].ack);
"Request rx/tx: %*lu/%*lu, ACK rx/tx: %*lu/%*lu\n",
IF_NAMESIZE, vlan_intf,
DHCP_COUNTER_WIDTH, rx_counters[DHCP_MESSAGE_TYPE_DISCOVER],
DHCP_COUNTER_WIDTH, tx_counters[DHCP_MESSAGE_TYPE_DISCOVER],
DHCP_COUNTER_WIDTH, rx_counters[DHCP_MESSAGE_TYPE_OFFER],
DHCP_COUNTER_WIDTH, tx_counters[DHCP_MESSAGE_TYPE_OFFER],
DHCP_COUNTER_WIDTH, rx_counters[DHCP_MESSAGE_TYPE_REQUEST],
DHCP_COUNTER_WIDTH, tx_counters[DHCP_MESSAGE_TYPE_REQUEST],
DHCP_COUNTER_WIDTH, rx_counters[DHCP_MESSAGE_TYPE_ACK],
DHCP_COUNTER_WIDTH, tx_counters[DHCP_MESSAGE_TYPE_ACK]);
} }
/** /**
* @code init_socket(context, intf); * @code init_socket(context, intf);
* *
* @brief initializes socket, bind it to interface and bpf program, and * @brief initializes socket, bind it to interface and bpf prgram, and
* associate with libevent base * associate with libevent base
* *
* @param context pointer to device (interface) context * @param context pointer to device (interface) context
@ -486,18 +402,6 @@ int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip)
return rv; return rv;
} }
/**
* @code dhcp_device_get_aggregate_context();
*
* @brief Accessor method
*
* @return pointer to aggregate device (interface) context
*/
dhcp_device_context_t* dhcp_device_get_aggregate_context()
{
return &aggregate_dev;
}
/** /**
* @code dhcp_device_init(context, intf, is_uplink); * @code dhcp_device_init(context, intf, is_uplink);
* *
@ -517,7 +421,8 @@ int dhcp_device_init(dhcp_device_context_t **context, const char *intf, uint8_t
dev_context->is_uplink = is_uplink; dev_context->is_uplink = is_uplink;
memset(dev_context->counters, 0, sizeof(dev_context->counters)); memset(&dev_context->counters, 0, sizeof(dev_context->counters));
memset(&dev_context->counters_snapshot, 0, sizeof(dev_context->counters_snapshot));
*context = dev_context; *context = dev_context;
rv = 0; rv = 0;
@ -592,44 +497,36 @@ void dhcp_device_shutdown(dhcp_device_context_t *context)
} }
/** /**
* @code dhcp_device_get_status(check_type, context); * @code dhcp_device_get_status(context);
* *
* @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate * @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate
* status * status
*/ */
dhcp_mon_status_t dhcp_device_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context) dhcp_mon_status_t dhcp_device_get_status(dhcp_device_context_t *context)
{ {
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY; dhcp_mon_status_t rv = 0;
if (context != NULL) { if (context != NULL) {
rv = dhcp_device_check_health(check_type, context->counters); rv = dhcp_device_validate(context->counters, context->counters_snapshot);
memcpy(context->counters_snapshot, context->counters, sizeof(context->counters_snapshot));
} else {
rv = dhcp_device_validate(glob_counters, glob_counters_snapshot);
memcpy(glob_counters_snapshot, glob_counters, sizeof(glob_counters_snapshot));
} }
return rv; return rv;
} }
/** /**
* @code dhcp_device_update_snapshot(context); * @code dhcp_device_print_status();
* *
* @brief Update device/interface counters snapshot * @brief prints status counters to syslog. If context is null, it will print aggregate status
*/
void dhcp_device_update_snapshot(dhcp_device_context_t *context)
{
if (context != NULL) {
memcpy(context->counters[DHCP_COUNTERS_SNAPSHOT],
context->counters[DHCP_COUNTERS_CURRENT],
sizeof(context->counters[DHCP_COUNTERS_SNAPSHOT]));
}
}
/**
* @code dhcp_device_print_status(context);
*
* @brief prints status counters to syslog.
*/ */
void dhcp_device_print_status(dhcp_device_context_t *context) void dhcp_device_print_status(dhcp_device_context_t *context)
{ {
if (context != NULL) { if (context != NULL) {
dhcp_print_counters(context->intf, context->counters); dhcp_print_counters(context->counters);
} else {
dhcp_print_counters(glob_counters);
} }
} }

View File

@ -17,23 +17,6 @@
#include <event2/buffer.h> #include <event2/buffer.h>
/**
* DHCP message types
**/
typedef enum
{
DHCP_MESSAGE_TYPE_DISCOVER = 1,
DHCP_MESSAGE_TYPE_OFFER = 2,
DHCP_MESSAGE_TYPE_REQUEST = 3,
DHCP_MESSAGE_TYPE_DECLINE = 4,
DHCP_MESSAGE_TYPE_ACK = 5,
DHCP_MESSAGE_TYPE_NAK = 6,
DHCP_MESSAGE_TYPE_RELEASE = 7,
DHCP_MESSAGE_TYPE_INFORM = 8,
DHCP_MESSAGE_TYPE_COUNT
} dhcp_message_type_t;
/** packet direction */ /** packet direction */
typedef enum typedef enum
{ {
@ -43,15 +26,6 @@ typedef enum
DHCP_DIR_COUNT DHCP_DIR_COUNT
} dhcp_packet_direction_t; } dhcp_packet_direction_t;
/** counters type */
typedef enum
{
DHCP_COUNTERS_CURRENT, /** DHCP current counters */
DHCP_COUNTERS_SNAPSHOT, /** DHCP snapshot counters */
DHCP_COUNTERS_COUNT
} dhcp_counters_type_t;
/** dhcp health status */ /** dhcp health status */
typedef enum typedef enum
{ {
@ -60,12 +34,14 @@ typedef enum
DHCP_MON_STATUS_INDETERMINATE, /** DHCP relay health could not be determined */ DHCP_MON_STATUS_INDETERMINATE, /** DHCP relay health could not be determined */
} dhcp_mon_status_t; } dhcp_mon_status_t;
/** dhcp check type */ /** DHCP device (interface) health counters */
typedef enum typedef struct
{ {
DHCP_MON_CHECK_NEGATIVE, /** Presence of relayed DHCP packets activity is flagged as unhealthy state */ uint64_t discover; /** DHCP discover packets */
DHCP_MON_CHECK_POSITIVE, /** Validate that received DORA packets are relayed */ uint64_t offer; /** DHCP offer packets */
} dhcp_mon_check_t; uint64_t request; /** DHCP request packets */
uint64_t ack; /** DHCP ack packets */
} dhcp_device_counters_t;
/** DHCP device (interface) context */ /** DHCP device (interface) context */
typedef struct typedef struct
@ -78,8 +54,10 @@ typedef struct
char intf[IF_NAMESIZE]; /** device (interface) name */ char intf[IF_NAMESIZE]; /** device (interface) name */
uint8_t *buffer; /** buffer used to read socket data */ uint8_t *buffer; /** buffer used to read socket data */
size_t snaplen; /** snap length or buffer size */ size_t snaplen; /** snap length or buffer size */
uint64_t counters[DHCP_COUNTERS_COUNT][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT]; dhcp_device_counters_t counters[DHCP_DIR_COUNT];
/** current/snapshot counters of DHCP packets */ /** current coutners of DORA packets */
dhcp_device_counters_t counters_snapshot[DHCP_DIR_COUNT];
/** counter snapshot */
} dhcp_device_context_t; } dhcp_device_context_t;
/** /**
@ -94,15 +72,6 @@ typedef struct
*/ */
int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip); int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip);
/**
* @code dhcp_device_get_aggregate_context();
*
* @brief Accessor method
*
* @return pointer to aggregate device (interface) context
*/
dhcp_device_context_t* dhcp_device_get_aggregate_context();
/** /**
* @code dhcp_device_init(context, intf, is_uplink); * @code dhcp_device_init(context, intf, is_uplink);
* *
@ -147,34 +116,22 @@ int dhcp_device_start_capture(dhcp_device_context_t *context,
void dhcp_device_shutdown(dhcp_device_context_t *context); void dhcp_device_shutdown(dhcp_device_context_t *context);
/** /**
* @code dhcp_device_get_status(check_type, context); * @code dhcp_device_get_status(context);
* *
* @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate * @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate
* status * status
* *
* @param check_type Type of validation * @param context Device (interface) context
* @param context Device (interface) context
* *
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE * @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/ */
dhcp_mon_status_t dhcp_device_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context); dhcp_mon_status_t dhcp_device_get_status(dhcp_device_context_t *context);
/** /**
* @code dhcp_device_update_snapshot(context); * @code dhcp_device_print_status();
*
* @param context Device (interface) context
*
* @brief Update device/interface counters snapshot
*/
void dhcp_device_update_snapshot(dhcp_device_context_t *context);
/**
* @code dhcp_device_print_status(context);
* *
* @brief prints status counters to syslog. If context is null, it will print aggregate status * @brief prints status counters to syslog. If context is null, it will print aggregate status
* *
* @param context Device (interface) context
*
* @return none * @return none
*/ */
void dhcp_device_print_status(dhcp_device_context_t *context); void dhcp_device_print_status(dhcp_device_context_t *context);

View File

@ -12,9 +12,6 @@
#include "dhcp_devman.h" #include "dhcp_devman.h"
/** Prefix appended to Aggregation device */
#define AGG_DEV_PREFIX "Agg-"
/** struct for interface information */ /** struct for interface information */
struct intf struct intf
{ {
@ -30,34 +27,22 @@ static LIST_HEAD(intf_list, intf) intfs;
static uint32_t dhcp_num_south_intf = 0; static uint32_t dhcp_num_south_intf = 0;
/** dhcp_num_north_intf number of north interfaces */ /** dhcp_num_north_intf number of north interfaces */
static uint32_t dhcp_num_north_intf = 0; static uint32_t dhcp_num_north_intf = 0;
/** dhcp_num_mgmt_intf number of mgmt interfaces */
static uint32_t dhcp_num_mgmt_intf = 0;
/** On Device vlan interface IP address corresponding vlan downlink IP /** On Device vlan interface IP address corresponding vlan downlink IP
* This IP is used to filter Offer/Ack packet coming from DHCP server */ * This IP is used to filter Offer/Ack packet coming from DHCP server */
static in_addr_t vlan_ip = 0; static in_addr_t vlan_ip = 0;
/** mgmt interface */ /** vlan interface name */
static struct intf *mgmt_intf = NULL; static char vlan_intf[IF_NAMESIZE] = "Undefined";
/** /**
* @code dhcp_devman_get_vlan_intf(); * @code dhcp_devman_get_vlan_intf();
* *
* Accessor method * Accessor method
*/ */
dhcp_device_context_t* dhcp_devman_get_agg_dev() const char* dhcp_devman_get_vlan_intf()
{ {
return dhcp_device_get_aggregate_context(); return vlan_intf;
}
/**
* @code dhcp_devman_get_mgmt_dev();
*
* Accessor method
*/
dhcp_device_context_t* dhcp_devman_get_mgmt_dev()
{
return mgmt_intf ? mgmt_intf->dev_context : NULL;
} }
/** /**
@ -101,42 +86,27 @@ void dhcp_devman_shutdown()
* *
* @brief adds interface to the device manager. * @brief adds interface to the device manager.
*/ */
int dhcp_devman_add_intf(const char *name, char intf_type) int dhcp_devman_add_intf(const char *name, uint8_t is_uplink)
{ {
int rv = -1; int rv = -1;
struct intf *dev = malloc(sizeof(struct intf)); struct intf *dev = malloc(sizeof(struct intf));
if (dev != NULL) { if (dev != NULL) {
dev->name = name; dev->name = name;
dev->is_uplink = intf_type != 'd'; dev->is_uplink = is_uplink;
if (is_uplink) {
switch (intf_type)
{
case 'u':
dhcp_num_north_intf++; dhcp_num_north_intf++;
break; } else {
case 'd':
dhcp_num_south_intf++; dhcp_num_south_intf++;
assert(dhcp_num_south_intf <= 1); assert(dhcp_num_south_intf <= 1);
break;
case 'm':
dhcp_num_mgmt_intf++;
assert(dhcp_num_mgmt_intf <= 1);
mgmt_intf = dev;
break;
default:
break;
} }
rv = dhcp_device_init(&dev->dev_context, dev->name, dev->is_uplink); rv = dhcp_device_init(&dev->dev_context, dev->name, dev->is_uplink);
if (rv == 0 && intf_type == 'd') { if (rv == 0 && !is_uplink) {
rv = dhcp_device_get_ip(dev->dev_context, &vlan_ip); rv = dhcp_device_get_ip(dev->dev_context, &vlan_ip);
dhcp_device_context_t *agg_dev = dhcp_device_get_aggregate_context(); strncpy(vlan_intf, name, sizeof(vlan_intf) - 1);
vlan_intf[sizeof(vlan_intf) - 1] = '\0';
strncpy(agg_dev->intf, AGG_DEV_PREFIX, sizeof(AGG_DEV_PREFIX));
strncpy(agg_dev->intf + sizeof(AGG_DEV_PREFIX) - 1, name, sizeof(agg_dev->intf) - sizeof(AGG_DEV_PREFIX));
agg_dev->intf[sizeof(agg_dev->intf) - 1] = '\0';
} }
LIST_INSERT_HEAD(&intfs, dev, entry); LIST_INSERT_HEAD(&intfs, dev, entry);
@ -182,41 +152,21 @@ int dhcp_devman_start_capture(size_t snaplen, struct event_base *base)
} }
/** /**
* @code dhcp_devman_get_status(check_type, context); * @code dhcp_devman_get_status();
* *
* @brief collects DHCP relay status info. * @brief collects DHCP relay status info.
*/ */
dhcp_mon_status_t dhcp_devman_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context) dhcp_mon_status_t dhcp_devman_get_status()
{ {
return dhcp_device_get_status(check_type, context); return dhcp_device_get_status(NULL);
} }
/** /**
* @code dhcp_devman_update_snapshot(context); * @code dhcp_devman_print_status();
* *
* @brief Update device/interface counters snapshot * @brief prints status counters to syslog
*/ */
void dhcp_devman_update_snapshot(dhcp_device_context_t *context) void dhcp_devman_print_status()
{ {
dhcp_device_update_snapshot(context); dhcp_device_print_status(NULL);
}
/**
* @code dhcp_devman_print_status(context);
*
* @brief prints status counters to syslog, if context is null, it prints status counters for all interfaces
*/
void dhcp_devman_print_status(dhcp_device_context_t *context)
{
if (context == NULL) {
struct intf *int_ptr;
LIST_FOREACH(int_ptr, &intfs, entry) {
dhcp_device_print_status(int_ptr->dev_context);
}
dhcp_device_print_status(dhcp_devman_get_agg_dev());
} else {
dhcp_device_print_status(context);
}
} }

View File

@ -36,18 +36,9 @@ void dhcp_devman_shutdown();
* *
* @brief Accessor method * @brief Accessor method
* *
* @return pointer to aggregate device (interface) context * @return pointer to vlan ip interface name
*/ */
dhcp_device_context_t* dhcp_devman_get_agg_dev(); const char* dhcp_devman_get_vlan_intf();
/**
* @code dhcp_devman_get_mgmt_intf_context();
*
* @brief Accessor method
*
* @return pointer to mgmt interface context
*/
dhcp_device_context_t* dhcp_devman_get_mgmt_dev();
/** /**
* @code dhcp_devman_add_intf(name, uplink); * @code dhcp_devman_add_intf(name, uplink);
@ -55,13 +46,11 @@ dhcp_device_context_t* dhcp_devman_get_mgmt_dev();
* @brief adds interface to the device manager. * @brief adds interface to the device manager.
* *
* @param name interface name * @param name interface name
* @param intf_type 'u' for uplink (north) interface * @param is_uplink true for uplink (north) interface
* 'd' for downlink (south) interface
* 'm' for mgmt interface
* *
* @return 0 on success, nonzero otherwise * @return 0 on success, nonzero otherwise
*/ */
int dhcp_devman_add_intf(const char *name, char intf_type); int dhcp_devman_add_intf(const char *name, uint8_t is_uplink);
/** /**
* @code dhcp_devman_start_capture(snaplen, base); * @code dhcp_devman_start_capture(snaplen, base);
@ -76,35 +65,21 @@ int dhcp_devman_add_intf(const char *name, char intf_type);
int dhcp_devman_start_capture(size_t snaplen, struct event_base *base); int dhcp_devman_start_capture(size_t snaplen, struct event_base *base);
/** /**
* @code dhcp_devman_get_status(check_type, context); * @code dhcp_devman_get_status();
* *
* @brief collects DHCP relay status info. * @brief collects DHCP relay status info.
* *
* @param check_type Type of validation
* @param context pointer to device (interface) context
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE * @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/ */
dhcp_mon_status_t dhcp_devman_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context); dhcp_mon_status_t dhcp_devman_get_status();
/** /**
* @code dhcp_devman_update_snapshot(context); * @code dhcp_devman_print_status();
*
* @param context Device (interface) context
*
* @brief Update device/interface counters snapshot
*/
void dhcp_devman_update_snapshot(dhcp_device_context_t *context);
/**
* @code dhcp_devman_print_status(context);
* *
* @brief prints status counters to syslog * @brief prints status counters to syslog
* *
* @param context pointer to device (interface) context
*
* @return none * @return none
*/ */
void dhcp_devman_print_status(dhcp_device_context_t *context); void dhcp_devman_print_status();
#endif /* DHCP_DEVMAN_H_ */ #endif /* DHCP_DEVMAN_H_ */

View File

@ -16,17 +16,8 @@
#include "dhcp_mon.h" #include "dhcp_mon.h"
#include "dhcp_devman.h" #include "dhcp_devman.h"
/** DHCP device/interface state */
typedef struct
{
dhcp_mon_check_t check_type; /** check type */
dhcp_device_context_t* (*get_context)(); /** functor to a device context accessor function */
int count; /** count in the number of unhealthy checks */
const char *msg; /** message to be printed if unhealthy state is determined */
} dhcp_mon_state_t;
/** window_interval_sec monitoring window for dhcp relay health checks */ /** window_interval_sec monitoring window for dhcp relay health checks */
static int window_interval_sec = 18; static int window_interval_sec = 12;
/** dhcp_unhealthy_max_count max count of consecutive unhealthy statuses before reporting to syslog */ /** dhcp_unhealthy_max_count max count of consecutive unhealthy statuses before reporting to syslog */
static int dhcp_unhealthy_max_count = 10; static int dhcp_unhealthy_max_count = 10;
/** libevent base struct */ /** libevent base struct */
@ -37,25 +28,6 @@ static struct event *ev_timeout = NULL;
static struct event *ev_sigint; static struct event *ev_sigint;
/** libevent SIGTERM signal event struct */ /** libevent SIGTERM signal event struct */
static struct event *ev_sigterm; static struct event *ev_sigterm;
/** libevent SIGUSR1 signal event struct */
static struct event *ev_sigusr1;
/** DHCP monitor state data for aggregate device for mgmt device */
static dhcp_mon_state_t state_data[] = {
[0] = {
.check_type = DHCP_MON_CHECK_POSITIVE,
.get_context = dhcp_devman_get_agg_dev,
.count = 0,
.msg = "dhcpmon detected disparity in DHCP Relay behavior. Duration: %d (sec) for vlan: '%s'\n"
},
[1] = {
.check_type = DHCP_MON_CHECK_NEGATIVE,
.get_context = dhcp_devman_get_mgmt_dev,
.count = 0,
.msg = "dhcpmon detected DHCP packets traveling through mgmt interface (please check BGP routes.)"
" Duration: %d (sec) for intf: '%s'\n"
}
};
/** /**
* @code signal_callback(fd, event, arg); * @code signal_callback(fd, event, arg);
@ -70,47 +42,9 @@ static dhcp_mon_state_t state_data[] = {
*/ */
static void signal_callback(evutil_socket_t fd, short event, void *arg) static void signal_callback(evutil_socket_t fd, short event, void *arg)
{ {
syslog(LOG_ALERT, "Received signal: '%s'\n", strsignal(fd)); syslog(LOG_ALERT, "Received signal %s\n", strsignal(fd));
dhcp_devman_print_status(NULL); dhcp_devman_print_status();
if ((fd == SIGTERM) || (fd == SIGINT)) { dhcp_mon_stop();
dhcp_mon_stop();
}
}
/**
* @code check_dhcp_relay_health(state_data);
*
* @brief check DHCP relay overall health
*
* @param state_data pointer to dhcpmon state data
*
* @return none
*/
static void check_dhcp_relay_health(dhcp_mon_state_t *state_data)
{
dhcp_device_context_t *context = state_data->get_context();
dhcp_mon_status_t dhcp_mon_status = dhcp_devman_get_status(state_data->check_type, context);
switch (dhcp_mon_status)
{
case DHCP_MON_STATUS_UNHEALTHY:
if (++state_data->count > dhcp_unhealthy_max_count) {
syslog(LOG_ALERT, state_data->msg, state_data->count * window_interval_sec, context->intf);
dhcp_devman_print_status(context);
}
break;
case DHCP_MON_STATUS_HEALTHY:
state_data->count = 0;
break;
case DHCP_MON_STATUS_INDETERMINATE:
if (state_data->count) {
state_data->count++;
}
break;
default:
syslog(LOG_ERR, "DHCP Relay returned unknown status %d\n", dhcp_mon_status);
break;
}
} }
/** /**
@ -126,12 +60,28 @@ static void check_dhcp_relay_health(dhcp_mon_state_t *state_data)
*/ */
static void timeout_callback(evutil_socket_t fd, short event, void *arg) static void timeout_callback(evutil_socket_t fd, short event, void *arg)
{ {
for (uint8_t i = 0; i < sizeof(state_data) / sizeof(*state_data); i++) { static int count = 0;
check_dhcp_relay_health(&state_data[i]); dhcp_mon_status_t dhcp_mon_status = dhcp_devman_get_status();
}
for (uint8_t i = 0; i < sizeof(state_data) / sizeof(*state_data); i++) { switch (dhcp_mon_status)
dhcp_devman_update_snapshot(state_data[i].get_context()); {
case DHCP_MON_STATUS_UNHEALTHY:
if (++count > dhcp_unhealthy_max_count) {
syslog(LOG_ALERT, "dhcpmon detected disparity in DHCP Relay behavior. Failure count: %d for vlan: '%s'\n",
count, dhcp_devman_get_vlan_intf());
dhcp_devman_print_status();
}
break;
case DHCP_MON_STATUS_HEALTHY:
if (count > 0) {
count = 0;
}
break;
case DHCP_MON_STATUS_INDETERMINATE:
break;
default:
syslog(LOG_ERR, "DHCP Relay returned unknown status %d\n", dhcp_mon_status);
break;
} }
} }
@ -168,12 +118,6 @@ int dhcp_mon_init(int window_sec, int max_count)
break; break;
} }
ev_sigusr1 = evsignal_new(base, SIGUSR1, signal_callback, base);
if (ev_sigusr1 == NULL) {
syslog(LOG_ERR, "Could not create SIGUSER1 libevent signal!\n");
break;
}
ev_timeout = event_new(base, -1, EV_PERSIST, timeout_callback, base); ev_timeout = event_new(base, -1, EV_PERSIST, timeout_callback, base);
if (ev_timeout == NULL) { if (ev_timeout == NULL) {
syslog(LOG_ERR, "Could not create libevent timer!\n"); syslog(LOG_ERR, "Could not create libevent timer!\n");
@ -196,12 +140,10 @@ void dhcp_mon_shutdown()
event_del(ev_timeout); event_del(ev_timeout);
event_del(ev_sigint); event_del(ev_sigint);
event_del(ev_sigterm); event_del(ev_sigterm);
event_del(ev_sigusr1);
event_free(ev_timeout); event_free(ev_timeout);
event_free(ev_sigint); event_free(ev_sigint);
event_free(ev_sigterm); event_free(ev_sigterm);
event_free(ev_sigusr1);
event_base_free(base); event_base_free(base);
} }
@ -231,11 +173,6 @@ int dhcp_mon_start(size_t snaplen)
break; break;
} }
if (evsignal_add(ev_sigusr1, NULL) != 0) {
syslog(LOG_ERR, "Could not add SIGUSR1 libevent signal!\n");
break;
}
struct timeval event_time = {.tv_sec = window_interval_sec, .tv_usec = 0}; struct timeval event_time = {.tv_sec = window_interval_sec, .tv_usec = 0};
if (evtimer_add(ev_timeout, &event_time) != 0) { if (evtimer_add(ev_timeout, &event_time) != 0) {
syslog(LOG_ERR, "Could not add event timer to libevent!\n"); syslog(LOG_ERR, "Could not add event timer to libevent!\n");

View File

@ -24,7 +24,7 @@
static const size_t dhcpmon_default_snaplen = 65535; static const size_t dhcpmon_default_snaplen = 65535;
/** dhcpmon_default_health_check_window: default value for a time window, during which DHCP DORA packet counts are being /** dhcpmon_default_health_check_window: default value for a time window, during which DHCP DORA packet counts are being
* collected */ * collected */
static const uint32_t dhcpmon_default_health_check_window = 18; static const uint32_t dhcpmon_default_health_check_window = 12;
/** dhcpmon_default_unhealthy_max_count: default max consecutive unhealthy status reported before reporting an issue /** dhcpmon_default_unhealthy_max_count: default max consecutive unhealthy status reported before reporting an issue
* with DHCP relay */ * with DHCP relay */
static const uint32_t dhcpmon_default_unhealthy_max_count = 10; static const uint32_t dhcpmon_default_unhealthy_max_count = 10;
@ -40,7 +40,7 @@ static const uint32_t dhcpmon_default_unhealthy_max_count = 10;
*/ */
static void usage(const char *prog) static void usage(const char *prog)
{ {
printf("Usage: %s -id <south interface> {-iu <north interface>}+ -im <mgmt interface> [-w <snapshot window in sec>]" printf("Usage: %s -id <south interface> {-iu <north interface>}+ [-w <snapshot window in sec>]"
"[-c <unhealthy status count>] [-s <snap length>] [-d]\n", prog); "[-c <unhealthy status count>] [-s <snap length>] [-d]\n", prog);
printf("where\n"); printf("where\n");
printf("\tsouth interface: is a vlan interface,\n"); printf("\tsouth interface: is a vlan interface,\n");
@ -50,7 +50,7 @@ static void usage(const char *prog)
printf("\tunhealthy status count: count of consecutive unhealthy status before writing an alert to syslog " printf("\tunhealthy status count: count of consecutive unhealthy status before writing an alert to syslog "
"(default %d),\n", "(default %d),\n",
dhcpmon_default_unhealthy_max_count); dhcpmon_default_unhealthy_max_count);
printf("\tsnap length: snap length of packet capture (default %ld),\n", dhcpmon_default_snaplen); printf("\tsnap length: snap length of packet capture (default %d),\n", dhcpmon_default_snaplen);
printf("\t-d: daemonize %s.\n", prog); printf("\t-d: daemonize %s.\n", prog);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@ -127,7 +127,7 @@ int main(int argc, char **argv)
usage(basename(argv[0])); usage(basename(argv[0]));
break; break;
case 'i': case 'i':
if (dhcp_devman_add_intf(argv[i + 1], argv[i][2]) != 0) { if (dhcp_devman_add_intf(argv[i + 1], argv[i][2] == 'u') != 0) {
usage(basename(argv[0])); usage(basename(argv[0]));
} }
i += 2; i += 2;

View File

@ -35,7 +35,7 @@ stderr_logfile=syslog
programs=dhcpmon-Vlan1000 programs=dhcpmon-Vlan1000
[program:dhcpmon-Vlan1000] [program:dhcpmon-Vlan1000]
command=/usr/sbin/dhcpmon -id Vlan1000 -iu PortChannel01 -iu PortChannel02 -iu PortChannel03 -iu PortChannel04 -im eth0 command=/usr/sbin/dhcpmon -id Vlan1000 -iu PortChannel01 -iu PortChannel02 -iu PortChannel03 -iu PortChannel04
priority=4 priority=4
autostart=false autostart=false
autorestart=false autorestart=false