This commit is contained in:
parent
1a9d2187c5
commit
b94e7894cc
@ -21,25 +21,51 @@
|
||||
#include <libexplain/ioctl.h>
|
||||
#include <linux/filter.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/ip6.h>
|
||||
|
||||
#include "dhcp_device.h"
|
||||
|
||||
/** DHCP versions flags */
|
||||
static bool dhcpv4_enabled;
|
||||
static bool dhcpv6_enabled;
|
||||
|
||||
/** Counter print width */
|
||||
#define DHCP_COUNTER_WIDTH 9
|
||||
|
||||
/** Start of Ether header of a captured frame */
|
||||
#define ETHER_START_OFFSET 0
|
||||
/** EtherType field offset from Ether header of a captured frame */
|
||||
#define ETHER_TYPE_OFFSET (ETHER_START_OFFSET + 12)
|
||||
/** Start of IP header of a captured frame */
|
||||
#define IP_START_OFFSET (ETHER_START_OFFSET + ETHER_HDR_LEN)
|
||||
/** Start of UDP header of a captured frame */
|
||||
#define UDP_START_OFFSET (IP_START_OFFSET + sizeof(struct ip))
|
||||
/** Start of DHCP header of a captured frame */
|
||||
#define DHCP_START_OFFSET (UDP_START_OFFSET + sizeof(struct udphdr))
|
||||
/** Start of DHCP Options segment of a captured frame */
|
||||
#define DHCP_OPTIONS_HEADER_SIZE 240
|
||||
/** Start of UDP header on IPv4 packet of a captured frame */
|
||||
#define UDPv4_START_OFFSET (IP_START_OFFSET + sizeof(struct ip))
|
||||
/** Start of DHCPv4 header of a captured frame */
|
||||
#define DHCPv4_START_OFFSET (UDPv4_START_OFFSET + sizeof(struct udphdr))
|
||||
/** Start of DHCPv4 Options segment of a captured frame */
|
||||
#define DHCPv4_OPTIONS_HEADER_SIZE 240
|
||||
/** Offset of DHCP GIADDR */
|
||||
#define DHCP_GIADDR_OFFSET 24
|
||||
|
||||
/** IPv6 link-local prefix */
|
||||
#define IPV6_LINK_LOCAL_PREFIX 0x80fe
|
||||
/** Start of UDP header on IPv6 packet of a captured frame */
|
||||
#define UDPv6_START_OFFSET (IP_START_OFFSET + sizeof(struct ip6_hdr))
|
||||
/** Start of DHCPv6 header of a captured frame */
|
||||
#define DHCPv6_START_OFFSET (UDPv6_START_OFFSET + sizeof(struct udphdr))
|
||||
/** Size of 'type' field on DHCPv6 header */
|
||||
#define DHCPv6_TYPE_LENGTH 1
|
||||
/** Size of DHCPv6 relay message header to first option */
|
||||
#define DHCPv6_RELAY_MSG_OPTIONS_OFFSET 34
|
||||
/** Size of 'option' field on DHCPv6 header */
|
||||
#define DHCPv6_OPTION_LENGTH 2
|
||||
/** Size of 'option length' field on DHCPv6 header */
|
||||
#define DHCPv6_OPTION_LEN_LENGTH 2
|
||||
/** DHCPv6 OPTION_RELAY_MSG */
|
||||
#define DHCPv6_OPTION_RELAY_MSG 9
|
||||
|
||||
#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_LDB (BPF_LD | BPF_B | BPF_ABS) /** bpf ldb Abs*/
|
||||
@ -49,34 +75,40 @@
|
||||
#define OP_JSET (BPF_JMP | BPF_JSET | BPF_K) /** bpf jset */
|
||||
#define OP_LDXB (BPF_LDX | BPF_B | BPF_MSH) /** bpf ldxb */
|
||||
|
||||
/** Berkeley Packet Filter program for "udp and (port 67 or port 68)".
|
||||
/** Berkeley Packet Filter program for "udp and (port 546 or port 547 or port 67 or port 68)".
|
||||
* This program is obtained using the following command tcpdump:
|
||||
* `tcpdump -dd "udp and (port 67 or port 68)"`
|
||||
* `tcpdump -dd "udp and (port 546 or port 547 or port 67 or port 68)"`
|
||||
*/
|
||||
static struct sock_filter dhcp_bpf_code[] = {
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x0000000c}, // (000) ldh [12]
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 7, .k = 0x000086dd}, // (001) jeq #0x86dd jt 2 jf 9
|
||||
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000014}, // (002) ldb [20]
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 18, .k = 0x00000011}, // (003) jeq #0x11 jt 4 jf 22
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000036}, // (004) ldh [54]
|
||||
{.code = OP_JEQ, .jt = 15, .jf = 0, .k = 0x00000043}, // (005) jeq #0x43 jt 21 jf 6
|
||||
{.code = OP_JEQ, .jt = 14, .jf = 0, .k = 0x00000044}, // (006) jeq #0x44 jt 21 jf 7
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000038}, // (007) ldh [56]
|
||||
{.code = OP_JEQ, .jt = 12, .jf = 11, .k = 0x00000043}, // (008) jeq #0x43 jt 21 jf 20
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 12, .k = 0x00000800}, // (009) jeq #0x800 jt 10 jf 22
|
||||
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000017}, // (010) ldb [23]
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 10, .k = 0x00000011}, // (011) jeq #0x11 jt 12 jf 22
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000014}, // (012) ldh [20]
|
||||
{.code = OP_JSET, .jt = 8, .jf = 0, .k = 0x00001fff}, // (013) jset #0x1fff jt 22 jf 14
|
||||
{.code = OP_LDXB, .jt = 0, .jf = 0, .k = 0x0000000e}, // (014) ldxb 4*([14]&0xf)
|
||||
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x0000000e}, // (015) ldh [x + 14]
|
||||
{.code = OP_JEQ, .jt = 4, .jf = 0, .k = 0x00000043}, // (016) jeq #0x43 jt 21 jf 17
|
||||
{.code = OP_JEQ, .jt = 3, .jf = 0, .k = 0x00000044}, // (017) jeq #0x44 jt 21 jf 18
|
||||
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x00000010}, // (018) ldh [x + 16]
|
||||
{.code = OP_JEQ, .jt = 1, .jf = 0, .k = 0x00000043}, // (019) jeq #0x43 jt 21 jf 20
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 1, .k = 0x00000044}, // (020) jeq #0x44 jt 21 jf 22
|
||||
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00040000}, // (021) ret #262144
|
||||
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00000000}, // (022) ret #0
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x0000000c}, // (000) ldh [12]
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 9, .k = 0x000086dd}, // (001) jeq #0x86dd jt 2 jf 11
|
||||
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000014}, // (002) ldb [20]
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 24, .k = 0x00000011}, // (003) jeq #0x11 jt 4 jf 28
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000036}, // (004) ldh [54]
|
||||
{.code = OP_JEQ, .jt = 21, .jf = 0, .k = 0x00000222}, // (005) jeq #0x222 jt 27 jf 6
|
||||
{.code = OP_JEQ, .jt = 20, .jf = 0, .k = 0x00000223}, // (006) jeq #0x223 jt 27 jf 7
|
||||
{.code = OP_JEQ, .jt = 19, .jf = 0, .k = 0x00000043}, // (007) jeq #0x43 jt 27 jf 8
|
||||
{.code = OP_JEQ, .jt = 18, .jf = 0, .k = 0x00000044}, // (008) jeq #0x44 jt 27 jf 9
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000038}, // (009) ldh [56]
|
||||
{.code = OP_JEQ, .jt = 16, .jf = 13, .k = 0x00000222}, // (010) jeq #0x222 jt 27 jf 24
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 16, .k = 0x00000800}, // (011) jeq #0x800 jt 12 jf 28
|
||||
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000017}, // (012) ldb [23]
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 14, .k = 0x00000011}, // (013) jeq #0x11 jt 14 jf 28
|
||||
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000014}, // (014) ldh [20]
|
||||
{.code = OP_JSET, .jt = 12, .jf = 0, .k = 0x00001fff}, // (015) jset #0x1fff jt 28 jf 16
|
||||
{.code = OP_LDXB, .jt = 0, .jf = 0, .k = 0x0000000e}, // (016) ldxb 4*([14]&0xf)
|
||||
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x0000000e}, // (017) ldh [x + 14]
|
||||
{.code = OP_JEQ, .jt = 8, .jf = 0, .k = 0x00000222}, // (018) jeq #0x222 jt 27 jf 19
|
||||
{.code = OP_JEQ, .jt = 7, .jf = 0, .k = 0x00000223}, // (019) jeq #0x223 jt 27 jf 20
|
||||
{.code = OP_JEQ, .jt = 6, .jf = 0, .k = 0x00000043}, // (020) jeq #0x43 jt 27 jf 21
|
||||
{.code = OP_JEQ, .jt = 5, .jf = 0, .k = 0x00000044}, // (021) jeq #0x44 jt 27 jf 22
|
||||
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x00000010}, // (022) ldh [x + 16]
|
||||
{.code = OP_JEQ, .jt = 3, .jf = 0, .k = 0x00000222}, // (023) jeq #0x222 jt 27 jf 24
|
||||
{.code = OP_JEQ, .jt = 2, .jf = 0, .k = 0x00000223}, // (024) jeq #0x223 jt 27 jf 25
|
||||
{.code = OP_JEQ, .jt = 1, .jf = 0, .k = 0x00000043}, // (025) jeq #0x43 jt 27 jf 26
|
||||
{.code = OP_JEQ, .jt = 0, .jf = 1, .k = 0x00000044}, // (026) jeq #0x44 jt 27 jf 28
|
||||
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00040000}, // (027) ret #262144
|
||||
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00000000}, // (028) ret
|
||||
};
|
||||
|
||||
/** Filter program socket struct */
|
||||
@ -89,16 +121,27 @@ static struct sock_fprog dhcp_sock_bfp = {
|
||||
*/
|
||||
static dhcp_device_context_t aggregate_dev = {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
|
||||
/** Monitored DHCPv4 message type */
|
||||
static dhcpv4_message_type_t v4_monitored_msgs[] = {
|
||||
DHCPv4_MESSAGE_TYPE_DISCOVER,
|
||||
DHCPv4_MESSAGE_TYPE_OFFER,
|
||||
DHCPv4_MESSAGE_TYPE_REQUEST,
|
||||
DHCPv4_MESSAGE_TYPE_ACK
|
||||
};
|
||||
|
||||
/** Number of monitored DHCP message type */
|
||||
static uint8_t monitored_msg_sz = sizeof(monitored_msgs) / sizeof(*monitored_msgs);
|
||||
/** Monitored DHCPv6 message type */
|
||||
static dhcpv6_message_type_t v6_monitored_msgs[] = {
|
||||
DHCPv6_MESSAGE_TYPE_SOLICIT,
|
||||
DHCPv6_MESSAGE_TYPE_ADVERTISE,
|
||||
DHCPv6_MESSAGE_TYPE_REQUEST,
|
||||
DHCPv6_MESSAGE_TYPE_REPLY
|
||||
};
|
||||
|
||||
/** Number of monitored DHCPv4 message type */
|
||||
static uint8_t v4_monitored_msg_sz = sizeof(v4_monitored_msgs) / sizeof(*v4_monitored_msgs);
|
||||
|
||||
/** Number of monitored DHCPv6 message type */
|
||||
static uint8_t v6_monitored_msg_sz = sizeof(v6_monitored_msgs) / sizeof(*v6_monitored_msgs);
|
||||
|
||||
/**
|
||||
* @code handle_dhcp_option_53(context, dhcp_option, dir, iphdr, dhcphdr);
|
||||
@ -123,27 +166,27 @@ static void handle_dhcp_option_53(dhcp_device_context_t *context,
|
||||
switch (dhcp_option[2])
|
||||
{
|
||||
// DHCP messages send by client
|
||||
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:
|
||||
case DHCPv4_MESSAGE_TYPE_DISCOVER:
|
||||
case DHCPv4_MESSAGE_TYPE_REQUEST:
|
||||
case DHCPv4_MESSAGE_TYPE_DECLINE:
|
||||
case DHCPv4_MESSAGE_TYPE_RELEASE:
|
||||
case DHCPv4_MESSAGE_TYPE_INFORM:
|
||||
giaddr = ntohl(dhcphdr[DHCP_GIADDR_OFFSET] << 24 | dhcphdr[DHCP_GIADDR_OFFSET + 1] << 16 |
|
||||
dhcphdr[DHCP_GIADDR_OFFSET + 2] << 8 | dhcphdr[DHCP_GIADDR_OFFSET + 3]);
|
||||
if ((context->giaddr_ip == giaddr && context->is_uplink && dir == DHCP_TX) ||
|
||||
(!context->is_uplink && dir == DHCP_RX && iphdr->ip_dst.s_addr == INADDR_BROADCAST)) {
|
||||
context->counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
aggregate_dev.counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
context->counters.v4counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
aggregate_dev.counters.v4counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
}
|
||||
break;
|
||||
// DHCP messages send by server
|
||||
case DHCP_MESSAGE_TYPE_OFFER:
|
||||
case DHCP_MESSAGE_TYPE_ACK:
|
||||
case DHCP_MESSAGE_TYPE_NAK:
|
||||
case DHCPv4_MESSAGE_TYPE_OFFER:
|
||||
case DHCPv4_MESSAGE_TYPE_ACK:
|
||||
case DHCPv4_MESSAGE_TYPE_NAK:
|
||||
if ((context->giaddr_ip == iphdr->ip_dst.s_addr && context->is_uplink && dir == DHCP_RX) ||
|
||||
(!context->is_uplink && dir == DHCP_TX)) {
|
||||
context->counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
aggregate_dev.counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
context->counters.v4counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
aggregate_dev.counters.v4counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -152,6 +195,43 @@ static void handle_dhcp_option_53(dhcp_device_context_t *context,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @code handle_dhcpv6_option(context, dhcp_option, dir);
|
||||
*
|
||||
* @brief handle the logic related to DHCPv6 option
|
||||
*
|
||||
* @param context Device (interface) context
|
||||
* @param dhcp_option pointer to DHCP option buffer space
|
||||
* @param dir packet direction
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void handle_dhcpv6_option(dhcp_device_context_t *context,
|
||||
const u_char dhcp_option,
|
||||
dhcp_packet_direction_t dir)
|
||||
{
|
||||
switch (dhcp_option)
|
||||
{
|
||||
case DHCPv6_MESSAGE_TYPE_SOLICIT:
|
||||
case DHCPv6_MESSAGE_TYPE_REQUEST:
|
||||
case DHCPv6_MESSAGE_TYPE_CONFIRM:
|
||||
case DHCPv6_MESSAGE_TYPE_RENEW:
|
||||
case DHCPv6_MESSAGE_TYPE_REBIND:
|
||||
case DHCPv6_MESSAGE_TYPE_RELEASE:
|
||||
case DHCPv6_MESSAGE_TYPE_DECLINE:
|
||||
case DHCPv6_MESSAGE_TYPE_ADVERTISE:
|
||||
case DHCPv6_MESSAGE_TYPE_REPLY:
|
||||
case DHCPv6_MESSAGE_TYPE_RECONFIGURE:
|
||||
case DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST:
|
||||
context->counters.v6counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option]++;
|
||||
aggregate_dev.counters.v6counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option]++;
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_WARNING, "handle_dhcpv6_option(%s): Unknown DHCPv6 option type %d", context->intf, dhcp_option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @code read_callback(fd, event, arg);
|
||||
*
|
||||
@ -171,16 +251,29 @@ static void read_callback(int fd, short event, void *arg)
|
||||
while ((event == EV_READ) &&
|
||||
((buffer_sz = recv(fd, context->buffer, context->snaplen, MSG_DONTWAIT)) > 0)) {
|
||||
struct ether_header *ethhdr = (struct ether_header*) context->buffer;
|
||||
struct ip *iphdr = (struct ip*) (context->buffer + IP_START_OFFSET);
|
||||
struct udphdr *udp = (struct udphdr*) (context->buffer + UDP_START_OFFSET);
|
||||
uint8_t *dhcphdr = context->buffer + DHCP_START_OFFSET;
|
||||
int dhcp_option_offset = DHCP_START_OFFSET + DHCP_OPTIONS_HEADER_SIZE;
|
||||
struct ip *iphdr;
|
||||
struct ip6_hdr *ipv6hdr;
|
||||
struct udphdr *udp;
|
||||
uint8_t *dhcphdr;
|
||||
int dhcp_option_offset;
|
||||
|
||||
if ((buffer_sz > UDP_START_OFFSET + sizeof(struct udphdr) + DHCP_OPTIONS_HEADER_SIZE) &&
|
||||
(ntohs(udp->len) > DHCP_OPTIONS_HEADER_SIZE)) {
|
||||
int dhcp_sz = ntohs(udp->len) < buffer_sz - UDP_START_OFFSET - sizeof(struct udphdr) ?
|
||||
ntohs(udp->len) : buffer_sz - UDP_START_OFFSET - sizeof(struct udphdr);
|
||||
int dhcp_option_sz = dhcp_sz - DHCP_OPTIONS_HEADER_SIZE;
|
||||
bool is_ipv4 = (ntohs(ethhdr->ether_type) == ETHERTYPE_IP);
|
||||
if (is_ipv4) {
|
||||
iphdr = (struct ip*) (context->buffer + IP_START_OFFSET);
|
||||
udp = (struct udphdr*) (context->buffer + UDPv4_START_OFFSET);
|
||||
dhcphdr = context->buffer + DHCPv4_START_OFFSET;
|
||||
dhcp_option_offset = DHCPv4_START_OFFSET + DHCPv4_OPTIONS_HEADER_SIZE;
|
||||
} else {
|
||||
ipv6hdr = (struct ip6_hdr*) (context->buffer + IP_START_OFFSET);
|
||||
udp = (struct udphdr*) (context->buffer + UDPv6_START_OFFSET);
|
||||
dhcphdr = context->buffer + DHCPv6_START_OFFSET;
|
||||
dhcp_option_offset = DHCPv6_START_OFFSET;
|
||||
}
|
||||
if (is_ipv4 && dhcpv4_enabled && (buffer_sz > UDPv4_START_OFFSET + sizeof(struct udphdr) + DHCPv4_OPTIONS_HEADER_SIZE) &&
|
||||
(ntohs(udp->len) > DHCPv4_OPTIONS_HEADER_SIZE)) {
|
||||
int dhcp_sz = ntohs(udp->len) < buffer_sz - UDPv4_START_OFFSET - sizeof(struct udphdr) ?
|
||||
ntohs(udp->len) : buffer_sz - UDPv4_START_OFFSET - sizeof(struct udphdr);
|
||||
int dhcp_option_sz = dhcp_sz - DHCPv4_OPTIONS_HEADER_SIZE;
|
||||
const u_char *dhcp_option = context->buffer + dhcp_option_offset;
|
||||
dhcp_packet_direction_t dir = (ethhdr->ether_shost[0] == context->mac[0] &&
|
||||
ethhdr->ether_shost[1] == context->mac[1] &&
|
||||
@ -214,6 +307,35 @@ static void read_callback(int fd, short event, void *arg)
|
||||
offset += dhcp_option[offset + 1] + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!is_ipv4 && dhcpv6_enabled && (buffer_sz > UDPv6_START_OFFSET + sizeof(struct udphdr) + DHCPv6_TYPE_LENGTH)) {
|
||||
const u_char* dhcp_option = context->buffer + dhcp_option_offset;
|
||||
dhcp_packet_direction_t dir = (ethhdr->ether_shost[0] == context->mac[0] &&
|
||||
ethhdr->ether_shost[1] == context->mac[1] &&
|
||||
ethhdr->ether_shost[2] == context->mac[2] &&
|
||||
ethhdr->ether_shost[3] == context->mac[3] &&
|
||||
ethhdr->ether_shost[4] == context->mac[4] &&
|
||||
ethhdr->ether_shost[5] == context->mac[5]) ?
|
||||
DHCP_TX : DHCP_RX;
|
||||
int offset = 0;
|
||||
uint16_t option = 0;
|
||||
// Get to inner DHCP header from encapsulated RELAY_FORWARD or RELAY_REPLY header
|
||||
while (dhcp_option[offset] == DHCPv6_MESSAGE_TYPE_RELAY_FORWARD || dhcp_option[offset] == DHCPv6_MESSAGE_TYPE_RELAY_REPLY)
|
||||
{
|
||||
// Get to DHCPv6_OPTION_RELAY_MSG from all options
|
||||
offset += DHCPv6_RELAY_MSG_OPTIONS_OFFSET;
|
||||
option = htons(*((uint16_t*)(&(dhcp_option[offset]))));
|
||||
|
||||
while (option != DHCPv6_OPTION_RELAY_MSG)
|
||||
{
|
||||
offset += DHCPv6_OPTION_LENGTH;
|
||||
// Add to offset the option length and get the next option ID
|
||||
offset += htons(*((uint16_t*)(&(dhcp_option[offset]))));
|
||||
option = htons(*((uint16_t*)(&(dhcp_option[offset]))));
|
||||
}
|
||||
offset += DHCPv6_OPTION_LENGTH + DHCPv6_OPTION_LEN_LENGTH;
|
||||
}
|
||||
handle_dhcpv6_option(context, dhcp_option[offset], dir);
|
||||
} else {
|
||||
syslog(LOG_WARNING, "read_callback(%s): read length (%ld) is too small to capture DHCP options",
|
||||
context->intf, buffer_sz);
|
||||
@ -222,29 +344,54 @@ static void read_callback(int fd, short event, void *arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_is_dhcp_inactive(counters);
|
||||
* @code dhcp_device_is_dhcp_inactive(v4counters, v6counters, type);
|
||||
*
|
||||
* @brief Check if there were no DHCP activity
|
||||
*
|
||||
* @param counters current/snapshot counter
|
||||
* @param v4counters current/snapshot v4counter
|
||||
*
|
||||
* @param v6counters current/snapshot v6counter
|
||||
*
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @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])
|
||||
static bool dhcp_device_is_dhcp_inactive(uint64_t v4counters[][DHCP_DIR_COUNT][DHCPv4_MESSAGE_TYPE_COUNT],
|
||||
uint64_t v6counters[][DHCP_DIR_COUNT][DHCPv6_MESSAGE_TYPE_COUNT],
|
||||
dhcp_type_t type)
|
||||
{
|
||||
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]];
|
||||
uint64_t *rx_counters;
|
||||
uint64_t *rx_counter_snapshot;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DHCPv4_TYPE:
|
||||
rx_counters = v4counters[DHCP_COUNTERS_CURRENT][DHCP_RX];
|
||||
rx_counter_snapshot = v4counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX];
|
||||
for (uint8_t i = 0; (i < v4_monitored_msg_sz) && rv; i++) {
|
||||
rv = rx_counters[v4_monitored_msgs[i]] == rx_counter_snapshot[v4_monitored_msgs[i]];
|
||||
}
|
||||
break;
|
||||
|
||||
case DHCPv6_TYPE:
|
||||
rx_counters = v6counters[DHCP_COUNTERS_CURRENT][DHCP_RX];
|
||||
rx_counter_snapshot = v6counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX];
|
||||
for (uint8_t i = 0; (i < v6_monitored_msg_sz) && rv; i++) {
|
||||
rv = rx_counters[v6_monitored_msgs[i]] == rx_counter_snapshot[v6_monitored_msgs[i]];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_ERR, "Unknown DHCP type %d\n", type);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_is_dhcp_msg_unhealthy(type, counters);
|
||||
* @code dhcp_device_is_dhcpv4_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.
|
||||
@ -254,34 +401,76 @@ static bool dhcp_device_is_dhcp_inactive(uint64_t counters[][DHCP_DIR_COUNT][DHC
|
||||
*
|
||||
* @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])
|
||||
static bool dhcp_device_is_dhcpv4_msg_unhealthy(dhcpv4_message_type_t type,
|
||||
uint64_t v4counters[][DHCP_DIR_COUNT][DHCPv4_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]) );
|
||||
return ((v4counters[DHCP_COUNTERS_CURRENT][DHCP_RX][type] > v4counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX][type]) &&
|
||||
(v4counters[DHCP_COUNTERS_CURRENT][DHCP_TX][type] <= v4counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX][type]) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_check_positive_health(counters, counters_snapshot);
|
||||
* @code dhcp_device_is_dhcpv6_msg_unhealthy(type, counters);
|
||||
*
|
||||
* @brief Check if DHCP relay is functioning properly for monitored messages (Discover, Offer, Request, ACK.)
|
||||
* @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_dhcpv6_msg_unhealthy(dhcpv6_message_type_t type,
|
||||
uint64_t v6counters[][DHCP_DIR_COUNT][DHCPv6_MESSAGE_TYPE_COUNT])
|
||||
{
|
||||
// check if DHCP message 'type' is being relayed
|
||||
return ((v6counters[DHCP_COUNTERS_CURRENT][DHCP_RX][type] > v6counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX][type]) &&
|
||||
(v6counters[DHCP_COUNTERS_CURRENT][DHCP_TX][type] <= v6counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX][type]) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_check_positive_health(v4counters, v6counters, type);
|
||||
*
|
||||
* @brief Check if DHCPv4/6 relay is functioning properly for monitored messages.
|
||||
* DHCPv4 (Discover, Offer, Request, ACK.) and DHCPv6 (Solicit, Advertise, Request, Reply).
|
||||
* For every rx of monitored messages, there should be increment of the same message type.
|
||||
*
|
||||
* @param counters current/snapshot counter
|
||||
* @param v4counters current/snapshot counter
|
||||
*
|
||||
* @param v6counters current/snapshot counter
|
||||
*
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @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])
|
||||
static dhcp_mon_status_t dhcp_device_check_positive_health(uint64_t v4counters[][DHCP_DIR_COUNT][DHCPv4_MESSAGE_TYPE_COUNT],
|
||||
uint64_t v6counters[][DHCP_DIR_COUNT][DHCPv6_MESSAGE_TYPE_COUNT],
|
||||
dhcp_type_t type)
|
||||
{
|
||||
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);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DHCPv4_TYPE:
|
||||
for (uint8_t i = 0; (i < v4_monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
|
||||
is_dhcp_unhealthy = dhcp_device_is_dhcpv4_msg_unhealthy(v4_monitored_msgs[i], v4counters);
|
||||
}
|
||||
break;
|
||||
|
||||
case DHCPv6_TYPE:
|
||||
for (uint8_t i = 0; (i < v6_monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
|
||||
is_dhcp_unhealthy = dhcp_device_is_dhcpv6_msg_unhealthy(v6_monitored_msgs[i], v6counters);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_ERR, "Unknown DHCP type %d\n", type);
|
||||
break;
|
||||
}
|
||||
|
||||
// if we have rx DORA then we should have corresponding tx DORA (DORA being relayed)
|
||||
// if we have rx DORA/SARR then we should have corresponding tx DORA/SARR (DORA/SARR being relayed)
|
||||
if (is_dhcp_unhealthy) {
|
||||
rv = DHCP_MON_STATUS_UNHEALTHY;
|
||||
}
|
||||
@ -290,27 +479,47 @@ static dhcp_mon_status_t dhcp_device_check_positive_health(uint64_t counters[][D
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_check_negative_health(counters);
|
||||
* @code dhcp_device_check_negative_health(v4counters, v6counters, type);
|
||||
*
|
||||
* @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_snapshot snapshot counters
|
||||
* @param v4counters current/snapshot counter
|
||||
* @param v6counters current/snapshot counter
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @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_check_negative_health(uint64_t v4counters[][DHCP_DIR_COUNT][DHCPv4_MESSAGE_TYPE_COUNT],
|
||||
uint64_t v6counters[][DHCP_DIR_COUNT][DHCPv6_MESSAGE_TYPE_COUNT],
|
||||
dhcp_type_t type)
|
||||
{
|
||||
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
|
||||
|
||||
uint64_t *tx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_TX];
|
||||
uint64_t *tx_counter_snapshot = counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX];
|
||||
|
||||
bool is_dhcp_unhealthy = false;
|
||||
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]];
|
||||
|
||||
uint64_t *tx_counters;
|
||||
uint64_t *tx_counter_snapshot;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DHCPv4_TYPE:
|
||||
tx_counters = v4counters[DHCP_COUNTERS_CURRENT][DHCP_TX];
|
||||
tx_counter_snapshot = v4counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX];
|
||||
for (uint8_t i = 0; (i < v4_monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
|
||||
is_dhcp_unhealthy = tx_counters[v4_monitored_msgs[i]] > tx_counter_snapshot[v4_monitored_msgs[i]];
|
||||
}
|
||||
break;
|
||||
case DHCPv6_TYPE:
|
||||
tx_counters = v6counters[DHCP_COUNTERS_CURRENT][DHCP_TX];
|
||||
tx_counter_snapshot = v6counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX];
|
||||
for (uint8_t i = 0; (i < v6_monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
|
||||
is_dhcp_unhealthy = tx_counters[v6_monitored_msgs[i]] > tx_counter_snapshot[v6_monitored_msgs[i]];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "Unknown DHCP type %d\n", type);
|
||||
break;
|
||||
}
|
||||
|
||||
// for negative validation, return unhealthy if DHCP packet are being
|
||||
@ -323,7 +532,7 @@ static dhcp_mon_status_t dhcp_device_check_negative_health(uint64_t counters[][D
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_check_health(check_type, counters, counters_snapshot);
|
||||
* @code dhcp_device_check_health(check_type, v4counters, v6counters, type);
|
||||
*
|
||||
* @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
|
||||
@ -332,59 +541,80 @@ static dhcp_mon_status_t dhcp_device_check_negative_health(uint64_t counters[][D
|
||||
* considered unhealthy.
|
||||
*
|
||||
* @param check_type type of health check
|
||||
* @param counters current/snapshot counter
|
||||
* @param v4counters current/snapshot counters
|
||||
* @param v6counters current/snapshot counters
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @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])
|
||||
uint64_t v4counters[][DHCP_DIR_COUNT][DHCPv4_MESSAGE_TYPE_COUNT],
|
||||
uint64_t v6counters[][DHCP_DIR_COUNT][DHCPv6_MESSAGE_TYPE_COUNT],
|
||||
dhcp_type_t type)
|
||||
{
|
||||
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
|
||||
|
||||
if (dhcp_device_is_dhcp_inactive(aggregate_dev.counters)) {
|
||||
if (dhcp_device_is_dhcp_inactive(aggregate_dev.counters.v4counters, aggregate_dev.counters.v6counters, type)) {
|
||||
rv = DHCP_MON_STATUS_INDETERMINATE;
|
||||
} else if (check_type == DHCP_MON_CHECK_POSITIVE) {
|
||||
rv = dhcp_device_check_positive_health(counters);
|
||||
rv = dhcp_device_check_positive_health(v4counters, v6counters, type);
|
||||
} else if (check_type == DHCP_MON_CHECK_NEGATIVE) {
|
||||
rv = dhcp_device_check_negative_health(counters);
|
||||
rv = dhcp_device_check_negative_health(v4counters, v6counters, type);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_print_counters(vlan_intf, type, counters);
|
||||
* @code dhcp_print_counters(vlan_intf, type, v4counters, v6counters);
|
||||
*
|
||||
* @brief prints DHCP counters to sylsog.
|
||||
*
|
||||
* @param vlan_intf vlan interface name
|
||||
* @param type counter type
|
||||
* @param counters interface counter
|
||||
* @param vlan_intf vlan interface name
|
||||
* @param type counter type
|
||||
* @param v4counters interface counter
|
||||
* @param v6counters interface counter
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void dhcp_print_counters(const char *vlan_intf,
|
||||
dhcp_counters_type_t type,
|
||||
uint64_t counters[][DHCP_MESSAGE_TYPE_COUNT])
|
||||
uint64_t v4counters[][DHCPv4_MESSAGE_TYPE_COUNT],
|
||||
uint64_t v6counters[][DHCPv6_MESSAGE_TYPE_COUNT])
|
||||
{
|
||||
static const char *counter_desc[DHCP_COUNTERS_COUNT] = {
|
||||
static const char *v4_counter_desc[DHCP_COUNTERS_COUNT] = {
|
||||
[DHCP_COUNTERS_CURRENT] = " Current",
|
||||
[DHCP_COUNTERS_SNAPSHOT] = "Snapshot"
|
||||
};
|
||||
static const char *v6_counter_desc[DHCP_COUNTERS_COUNT] = {
|
||||
[DHCP_COUNTERS_CURRENT] = " Current",
|
||||
[DHCP_COUNTERS_SNAPSHOT] = "Snapshot"
|
||||
};
|
||||
|
||||
syslog(
|
||||
LOG_NOTICE,
|
||||
"[%*s-%*s rx/tx] Discover: %*lu/%*lu, Offer: %*lu/%*lu, Request: %*lu/%*lu, ACK: %*lu/%*lu\n",
|
||||
"DHCPv4 [%*s-%*s rx/tx] Discover: %*lu/%*lu, Offer: %*lu/%*lu, Request: %*lu/%*lu, ACK: %*lu/%*lu\n\
|
||||
DHCPv6 [%*s-%*s rx/tx] Solicit: %*lu/%*lu, Advertise: %*lu/%*lu, Request: %*lu/%*lu, Reply: %*lu/%*lu\n",
|
||||
IF_NAMESIZE, vlan_intf,
|
||||
(int) strlen(counter_desc[type]), counter_desc[type],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_DISCOVER],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_DISCOVER],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_OFFER],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_OFFER],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_REQUEST],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_REQUEST],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_ACK],
|
||||
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_ACK]
|
||||
(int) strlen(v4_counter_desc[type]), v4_counter_desc[type],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_RX][DHCPv4_MESSAGE_TYPE_DISCOVER],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_TX][DHCPv4_MESSAGE_TYPE_DISCOVER],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_RX][DHCPv4_MESSAGE_TYPE_OFFER],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_TX][DHCPv4_MESSAGE_TYPE_OFFER],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_RX][DHCPv4_MESSAGE_TYPE_REQUEST],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_TX][DHCPv4_MESSAGE_TYPE_REQUEST],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_RX][DHCPv4_MESSAGE_TYPE_ACK],
|
||||
DHCP_COUNTER_WIDTH, v4counters[DHCP_TX][DHCPv4_MESSAGE_TYPE_ACK],
|
||||
IF_NAMESIZE, vlan_intf,
|
||||
(int) strlen(v6_counter_desc[type]), v6_counter_desc[type],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_RX][DHCPv6_MESSAGE_TYPE_SOLICIT],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_TX][DHCPv6_MESSAGE_TYPE_SOLICIT],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_RX][DHCPv6_MESSAGE_TYPE_ADVERTISE],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_TX][DHCPv6_MESSAGE_TYPE_ADVERTISE],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_RX][DHCPv6_MESSAGE_TYPE_REQUEST],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_TX][DHCPv6_MESSAGE_TYPE_REQUEST],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_RX][DHCPv6_MESSAGE_TYPE_REPLY],
|
||||
DHCP_COUNTER_WIDTH, v6counters[DHCP_TX][DHCPv6_MESSAGE_TYPE_REPLY]
|
||||
);
|
||||
}
|
||||
|
||||
@ -454,12 +684,12 @@ int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context)
|
||||
strncpy(ifr.ifr_name, context->intf, sizeof(ifr.ifr_name) - 1);
|
||||
ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
|
||||
|
||||
// Get network address
|
||||
// Get v4 network address
|
||||
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
|
||||
syslog(LOG_ALERT, "ioctl: %s", explain_ioctl(fd, SIOCGIFADDR, &ifr));
|
||||
break;
|
||||
}
|
||||
context->ip = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
|
||||
context->ipv4 = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
|
||||
|
||||
// Get mac address
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
|
||||
@ -470,6 +700,30 @@ int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context)
|
||||
|
||||
close(fd);
|
||||
|
||||
// Get v6 network address
|
||||
memset(&context->ipv6, 0, sizeof(context->ipv6));
|
||||
struct ifaddrs *ifa, *ifa_tmp;
|
||||
|
||||
if (getifaddrs(&ifa) == -1) {
|
||||
syslog(LOG_ALERT, "getifaddrs failed");
|
||||
break;
|
||||
}
|
||||
|
||||
ifa_tmp = ifa;
|
||||
while (ifa_tmp) {
|
||||
// Check if current interface has a valid IPv6 address (not link local address)
|
||||
if ((strncmp(ifa_tmp->ifa_name, context->intf, sizeof(context->intf)) == 0) &&
|
||||
(ifa_tmp->ifa_addr) &&
|
||||
(ifa_tmp->ifa_addr->sa_family == AF_INET6) &&
|
||||
(((struct sockaddr_in6*)(ifa_tmp->ifa_addr))->sin6_addr.__in6_u.__u6_addr16[0] != IPV6_LINK_LOCAL_PREFIX)) {
|
||||
|
||||
struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr;
|
||||
memcpy(&context->ipv6, &in6->sin6_addr, sizeof(context->ipv6));
|
||||
}
|
||||
ifa_tmp = ifa_tmp->ifa_next;
|
||||
}
|
||||
freeifaddrs(ifa);
|
||||
|
||||
rv = 0;
|
||||
} while (0);
|
||||
|
||||
@ -477,20 +731,41 @@ int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context)
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_get_ip(context);
|
||||
* @code dhcp_device_get_ipv4(context);
|
||||
*
|
||||
* @brief Accessor method
|
||||
*
|
||||
* @param context pointer to device (interface) context
|
||||
*
|
||||
* @return interface IP
|
||||
* @return interface IPv4
|
||||
*/
|
||||
int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip)
|
||||
int dhcp_device_get_ipv4(dhcp_device_context_t *context, in_addr_t *ip)
|
||||
{
|
||||
int rv = -1;
|
||||
|
||||
if (context != NULL && ip != NULL) {
|
||||
*ip = context->ip;
|
||||
*ip = context->ipv4;
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_get_ipv6(context);
|
||||
*
|
||||
* @brief Accessor method
|
||||
*
|
||||
* @param context pointer to device (interface) context
|
||||
*
|
||||
* @return interface IPv6
|
||||
*/
|
||||
int dhcp_device_get_ipv6(dhcp_device_context_t *context, struct in6_addr *ip)
|
||||
{
|
||||
int rv = -1;
|
||||
|
||||
if (context != NULL && ip != NULL) {
|
||||
*ip = context->ipv6;
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
@ -528,7 +803,8 @@ int dhcp_device_init(dhcp_device_context_t **context, const char *intf, uint8_t
|
||||
|
||||
dev_context->is_uplink = is_uplink;
|
||||
|
||||
memset(dev_context->counters, 0, sizeof(dev_context->counters));
|
||||
memset(dev_context->counters.v4counters, 0, sizeof(dev_context->counters.v4counters));
|
||||
memset(dev_context->counters.v6counters, 0, sizeof(dev_context->counters.v6counters));
|
||||
|
||||
*context = dev_context;
|
||||
rv = 0;
|
||||
@ -543,14 +819,15 @@ int dhcp_device_init(dhcp_device_context_t **context, const char *intf, uint8_t
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_start_capture(context, snaplen, base, giaddr_ip);
|
||||
* @code dhcp_device_start_capture(context, snaplen, base, giaddr_ip, v6_vlan_ip);
|
||||
*
|
||||
* @brief starts packet capture on this interface
|
||||
*/
|
||||
int dhcp_device_start_capture(dhcp_device_context_t *context,
|
||||
size_t snaplen,
|
||||
struct event_base *base,
|
||||
in_addr_t giaddr_ip)
|
||||
in_addr_t giaddr_ip,
|
||||
struct in6_addr v6_vlan_ip)
|
||||
{
|
||||
int rv = -1;
|
||||
|
||||
@ -560,12 +837,20 @@ int dhcp_device_start_capture(dhcp_device_context_t *context,
|
||||
break;
|
||||
}
|
||||
|
||||
if (snaplen < UDP_START_OFFSET + sizeof(struct udphdr) + DHCP_OPTIONS_HEADER_SIZE) {
|
||||
syslog(LOG_ALERT, "dhcp_device_start_capture(%s): snap length is too low to capture DHCP options", context->intf);
|
||||
// snaplen check for DHCPv4 size
|
||||
if (dhcpv4_enabled && snaplen < UDPv4_START_OFFSET + sizeof(struct udphdr) + DHCPv4_OPTIONS_HEADER_SIZE) {
|
||||
syslog(LOG_ALERT, "dhcp_device_start_capture(%s): snap length is too low to capture DHCPv4 options", context->intf);
|
||||
break;
|
||||
}
|
||||
|
||||
// snaplen check for DHCPv6 size - DHCPv6 message type is the first byte of the udp payload
|
||||
if (dhcpv6_enabled && snaplen < DHCPv6_START_OFFSET + 1) {
|
||||
syslog(LOG_ALERT, "dhcp_device_start_capture(%s): snap length is too low to capture DHCPv6 option", context->intf);
|
||||
break;
|
||||
}
|
||||
|
||||
context->giaddr_ip = giaddr_ip;
|
||||
context->v6_vlan_ip = v6_vlan_ip;
|
||||
|
||||
context->buffer = (uint8_t *) malloc(snaplen);
|
||||
if (context->buffer == NULL) {
|
||||
@ -603,17 +888,17 @@ void dhcp_device_shutdown(dhcp_device_context_t *context)
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_get_status(check_type, context);
|
||||
* @code dhcp_device_get_status(check_type, context, type);
|
||||
*
|
||||
* @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate
|
||||
* 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_mon_check_t check_type, dhcp_device_context_t *context, dhcp_type_t type)
|
||||
{
|
||||
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
|
||||
|
||||
if (context != NULL) {
|
||||
rv = dhcp_device_check_health(check_type, context->counters);
|
||||
rv = dhcp_device_check_health(check_type, context->counters.v4counters, context->counters.v6counters, type);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -627,9 +912,17 @@ dhcp_mon_status_t dhcp_device_get_status(dhcp_mon_check_t check_type, dhcp_devic
|
||||
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]));
|
||||
if (dhcpv4_enabled) {
|
||||
memcpy(context->counters.v4counters[DHCP_COUNTERS_SNAPSHOT],
|
||||
context->counters.v4counters[DHCP_COUNTERS_CURRENT],
|
||||
sizeof(context->counters.v4counters[DHCP_COUNTERS_SNAPSHOT]));
|
||||
}
|
||||
|
||||
if (dhcpv6_enabled) {
|
||||
memcpy(context->counters.v6counters[DHCP_COUNTERS_SNAPSHOT],
|
||||
context->counters.v6counters[DHCP_COUNTERS_CURRENT],
|
||||
sizeof(context->counters.v6counters[DHCP_COUNTERS_SNAPSHOT]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,6 +934,17 @@ void dhcp_device_update_snapshot(dhcp_device_context_t *context)
|
||||
void dhcp_device_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type)
|
||||
{
|
||||
if (context != NULL) {
|
||||
dhcp_print_counters(context->intf, type, context->counters[type]);
|
||||
dhcp_print_counters(context->intf, type, context->counters.v4counters[type], context->counters.v6counters[type]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_device_active_types(dhcpv4, dhcpv6);
|
||||
*
|
||||
* @brief update local variables with active protocols
|
||||
*/
|
||||
void dhcp_device_active_types(bool dhcpv4, bool dhcpv6)
|
||||
{
|
||||
dhcpv4_enabled = dhcpv4;
|
||||
dhcpv6_enabled = dhcpv6;
|
||||
}
|
||||
|
@ -18,21 +18,43 @@
|
||||
|
||||
|
||||
/**
|
||||
* DHCP message types
|
||||
* DHCPv4 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,
|
||||
DHCPv4_MESSAGE_TYPE_DISCOVER = 1,
|
||||
DHCPv4_MESSAGE_TYPE_OFFER = 2,
|
||||
DHCPv4_MESSAGE_TYPE_REQUEST = 3,
|
||||
DHCPv4_MESSAGE_TYPE_DECLINE = 4,
|
||||
DHCPv4_MESSAGE_TYPE_ACK = 5,
|
||||
DHCPv4_MESSAGE_TYPE_NAK = 6,
|
||||
DHCPv4_MESSAGE_TYPE_RELEASE = 7,
|
||||
DHCPv4_MESSAGE_TYPE_INFORM = 8,
|
||||
|
||||
DHCP_MESSAGE_TYPE_COUNT
|
||||
} dhcp_message_type_t;
|
||||
DHCPv4_MESSAGE_TYPE_COUNT
|
||||
} dhcpv4_message_type_t;
|
||||
|
||||
/**
|
||||
* DHCPv6 message types
|
||||
**/
|
||||
typedef enum
|
||||
{
|
||||
DHCPv6_MESSAGE_TYPE_SOLICIT = 1,
|
||||
DHCPv6_MESSAGE_TYPE_ADVERTISE = 2,
|
||||
DHCPv6_MESSAGE_TYPE_REQUEST = 3,
|
||||
DHCPv6_MESSAGE_TYPE_CONFIRM = 4,
|
||||
DHCPv6_MESSAGE_TYPE_RENEW = 5,
|
||||
DHCPv6_MESSAGE_TYPE_REBIND = 6,
|
||||
DHCPv6_MESSAGE_TYPE_REPLY = 7,
|
||||
DHCPv6_MESSAGE_TYPE_RELEASE = 8,
|
||||
DHCPv6_MESSAGE_TYPE_DECLINE = 9,
|
||||
DHCPv6_MESSAGE_TYPE_RECONFIGURE = 10,
|
||||
DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST = 11,
|
||||
DHCPv6_MESSAGE_TYPE_RELAY_FORWARD = 12,
|
||||
DHCPv6_MESSAGE_TYPE_RELAY_REPLY = 13,
|
||||
|
||||
DHCPv6_MESSAGE_TYPE_COUNT
|
||||
} dhcpv6_message_type_t;
|
||||
|
||||
/** packet direction */
|
||||
typedef enum
|
||||
@ -60,6 +82,13 @@ typedef enum
|
||||
DHCP_MON_STATUS_INDETERMINATE, /** DHCP relay health could not be determined */
|
||||
} dhcp_mon_status_t;
|
||||
|
||||
/** dhcp type */
|
||||
typedef enum
|
||||
{
|
||||
DHCPv4_TYPE,
|
||||
DHCPv6_TYPE,
|
||||
} dhcp_type_t;
|
||||
|
||||
/** dhcp check type */
|
||||
typedef enum
|
||||
{
|
||||
@ -67,19 +96,28 @@ typedef enum
|
||||
DHCP_MON_CHECK_POSITIVE, /** Validate that received DORA packets are relayed */
|
||||
} dhcp_mon_check_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t v4counters[DHCP_COUNTERS_COUNT][DHCP_DIR_COUNT][DHCPv4_MESSAGE_TYPE_COUNT];
|
||||
/** current/snapshot counters of DHCPv4 packets */
|
||||
uint64_t v6counters[DHCP_COUNTERS_COUNT][DHCP_DIR_COUNT][DHCPv6_MESSAGE_TYPE_COUNT];
|
||||
/** current/snapshot counters of DHCPv6 packets */
|
||||
} counters_t;
|
||||
|
||||
/** DHCP device (interface) context */
|
||||
typedef struct
|
||||
{
|
||||
int sock; /** Raw socket associated with this device/interface */
|
||||
in_addr_t ip; /** network address of this device (interface) */
|
||||
in_addr_t ipv4; /** ipv4 network address of this device (interface) */
|
||||
struct in6_addr ipv6; /** ipv6 network address of this device (interface) */
|
||||
uint8_t mac[ETHER_ADDR_LEN]; /** hardware address of this device (interface) */
|
||||
in_addr_t giaddr_ip; /** Gateway IP address */
|
||||
in_addr_t giaddr_ip; /** Gateway IPv4 address */
|
||||
struct in6_addr v6_vlan_ip; /** Vlan IPv6 address */
|
||||
uint8_t is_uplink; /** north interface? */
|
||||
char intf[IF_NAMESIZE]; /** device (interface) name */
|
||||
uint8_t *buffer; /** buffer used to read socket data */
|
||||
size_t snaplen; /** snap length or buffer size */
|
||||
uint64_t counters[DHCP_COUNTERS_COUNT][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT];
|
||||
/** current/snapshot counters of DHCP packets */
|
||||
counters_t counters; /** counters for DHCPv4/6 packets */
|
||||
} dhcp_device_context_t;
|
||||
|
||||
/**
|
||||
@ -94,16 +132,28 @@ typedef struct
|
||||
int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_get_ip(context, ip);
|
||||
* @code dhcp_device_get_ipv4(context, ip);
|
||||
*
|
||||
* @brief Accessor method
|
||||
*
|
||||
* @param context pointer to device (interface) context
|
||||
* @param ip(out) pointer to device IP
|
||||
* @param ip(out) pointer to device IPv4
|
||||
*
|
||||
* @return 0 on success, otherwise for failure
|
||||
*/
|
||||
int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip);
|
||||
int dhcp_device_get_ipv4(dhcp_device_context_t *context, in_addr_t *ip);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_get_ipv6(context, ip);
|
||||
*
|
||||
* @brief Accessor method
|
||||
*
|
||||
* @param context pointer to device (interface) context
|
||||
* @param ip(out) pointer to device IPv6
|
||||
*
|
||||
* @return 0 on success, otherwise for failure
|
||||
*/
|
||||
int dhcp_device_get_ipv6(dhcp_device_context_t *context, struct in6_addr *ip);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_get_aggregate_context();
|
||||
@ -130,7 +180,7 @@ int dhcp_device_init(dhcp_device_context_t **context,
|
||||
uint8_t is_uplink);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_start_capture(context, snaplen, base, giaddr_ip);
|
||||
* @code dhcp_device_start_capture(context, snaplen, base, giaddr_ip, v6_vlan_ip);
|
||||
*
|
||||
* @brief starts packet capture on this interface
|
||||
*
|
||||
@ -138,13 +188,15 @@ int dhcp_device_init(dhcp_device_context_t **context,
|
||||
* @param snaplen length of packet capture
|
||||
* @param base pointer to libevent base
|
||||
* @param giaddr_ip gateway IP address
|
||||
* @param v6_vlan_ip vlan IPv6 address
|
||||
*
|
||||
* @return 0 on success, otherwise for failure
|
||||
*/
|
||||
int dhcp_device_start_capture(dhcp_device_context_t *context,
|
||||
size_t snaplen,
|
||||
struct event_base *base,
|
||||
in_addr_t giaddr_ip);
|
||||
in_addr_t giaddr_ip,
|
||||
struct in6_addr v6_vlan_ip);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_shutdown(context);
|
||||
@ -158,17 +210,18 @@ int dhcp_device_start_capture(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(check_type, context, type);
|
||||
*
|
||||
* @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate
|
||||
* status
|
||||
*
|
||||
* @param check_type Type of validation
|
||||
* @param context Device (interface) context
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @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_mon_check_t check_type, dhcp_device_context_t *context, dhcp_type_t type);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_update_snapshot(context);
|
||||
@ -185,10 +238,21 @@ void dhcp_device_update_snapshot(dhcp_device_context_t *context);
|
||||
* @brief prints status counters to syslog. If context is null, it will print aggregate status
|
||||
*
|
||||
* @param context Device (interface) context
|
||||
* @param counters_type Counter type to be printed
|
||||
* @param type Counter type to be printed
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void dhcp_device_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type);
|
||||
|
||||
/**
|
||||
* @code dhcp_device_active_types(dhcpv4, dhcpv6);
|
||||
*
|
||||
* @brief update local variables with active protocols
|
||||
*
|
||||
* @param dhcpv4 DHCPv4 enable flag
|
||||
* @param dhcpv6 DHCPv6 enable flag
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void dhcp_device_active_types(bool dhcpv4, bool dhcpv6);
|
||||
#endif /* DHCP_DEVICE_H_ */
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <syslog.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dhcp_devman.h"
|
||||
|
||||
@ -35,7 +37,8 @@ static uint32_t dhcp_num_mgmt_intf = 0;
|
||||
|
||||
/** On Device vlan interface IP address corresponding vlan downlink IP
|
||||
* This IP is used to filter Offer/Ack packet coming from DHCP server */
|
||||
static in_addr_t vlan_ip = 0;
|
||||
static in_addr_t v4_vlan_ip = 0;
|
||||
static struct in6_addr v6_vlan_ip = {0};
|
||||
|
||||
/* Device loopback interface ip, which will be used as the giaddr in dual tor setup. */
|
||||
static in_addr_t loopback_ip = 0;
|
||||
@ -136,7 +139,8 @@ int dhcp_devman_add_intf(const char *name, char intf_type)
|
||||
|
||||
rv = dhcp_device_init(&dev->dev_context, dev->name, dev->is_uplink);
|
||||
if (rv == 0 && intf_type == 'd') {
|
||||
rv = dhcp_device_get_ip(dev->dev_context, &vlan_ip);
|
||||
rv = dhcp_device_get_ipv4(dev->dev_context, &v4_vlan_ip);
|
||||
rv = dhcp_device_get_ipv6(dev->dev_context, &v6_vlan_ip);
|
||||
|
||||
dhcp_device_context_t *agg_dev = dhcp_device_get_aggregate_context();
|
||||
|
||||
@ -174,7 +178,7 @@ int dhcp_devman_setup_dual_tor_mode(const char *name)
|
||||
}
|
||||
|
||||
if (initialize_intf_mac_and_ip_addr(&loopback_intf_context) == 0 &&
|
||||
dhcp_device_get_ip(&loopback_intf_context, &loopback_ip) == 0) {
|
||||
dhcp_device_get_ipv4(&loopback_intf_context, &loopback_ip) == 0) {
|
||||
dual_tor_mode = 1;
|
||||
} else {
|
||||
syslog(LOG_ALERT, "failed to retrieve ip addr for loopback interface (%s)", name);
|
||||
@ -197,11 +201,13 @@ int dhcp_devman_start_capture(size_t snaplen, struct event_base *base)
|
||||
|
||||
if ((dhcp_num_south_intf == 1) && (dhcp_num_north_intf >= 1)) {
|
||||
LIST_FOREACH(int_ptr, &intfs, entry) {
|
||||
rv = dhcp_device_start_capture(int_ptr->dev_context, snaplen, base, dual_tor_mode ? loopback_ip : vlan_ip);
|
||||
rv = dhcp_device_start_capture(int_ptr->dev_context, snaplen, base, dual_tor_mode ? loopback_ip : v4_vlan_ip, v6_vlan_ip);
|
||||
if (rv == 0) {
|
||||
char ipv6_addr[INET6_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET6, &int_ptr->dev_context->ipv6, ipv6_addr, sizeof(ipv6_addr));
|
||||
syslog(LOG_INFO,
|
||||
"Capturing DHCP packets on interface %s, ip: 0x%08x, mac [%02x:%02x:%02x:%02x:%02x:%02x] \n",
|
||||
int_ptr->name, int_ptr->dev_context->ip, int_ptr->dev_context->mac[0],
|
||||
"Capturing DHCP packets on interface %s, ipv4: 0x%08x, ipv6: %s, mac [%02x:%02x:%02x:%02x:%02x:%02x] \n",
|
||||
int_ptr->name, int_ptr->dev_context->ipv4, ipv6_addr, int_ptr->dev_context->mac[0],
|
||||
int_ptr->dev_context->mac[1], int_ptr->dev_context->mac[2], int_ptr->dev_context->mac[3],
|
||||
int_ptr->dev_context->mac[4], int_ptr->dev_context->mac[5]);
|
||||
}
|
||||
@ -219,13 +225,13 @@ 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(check_type, context, type);
|
||||
*
|
||||
* @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(dhcp_mon_check_t check_type, dhcp_device_context_t *context, dhcp_type_t type)
|
||||
{
|
||||
return dhcp_device_get_status(check_type, context);
|
||||
return dhcp_device_get_status(check_type, context, type);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,3 +273,13 @@ void dhcp_devman_print_status(dhcp_device_context_t *context, dhcp_counters_type
|
||||
dhcp_device_print_status(context, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @code dhcp_devman_active_types(dhcpv4, dhcpv6);
|
||||
*
|
||||
* @brief update local variables with active protocols
|
||||
*/
|
||||
void dhcp_devman_active_types(bool dhcpv4, bool dhcpv6)
|
||||
{
|
||||
dhcp_device_active_types(dhcpv4, dhcpv6);
|
||||
}
|
||||
|
@ -87,16 +87,17 @@ int dhcp_devman_setup_dual_tor_mode(const char *name);
|
||||
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(check_type, context, type);
|
||||
*
|
||||
* @brief collects DHCP relay status info.
|
||||
*
|
||||
* @param check_type Type of validation
|
||||
* @param context pointer to device (interface) context
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @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(dhcp_mon_check_t check_type, dhcp_device_context_t *context, dhcp_type_t type);
|
||||
|
||||
/**
|
||||
* @code dhcp_devman_update_snapshot(context);
|
||||
@ -119,4 +120,16 @@ void dhcp_devman_update_snapshot(dhcp_device_context_t *context);
|
||||
*/
|
||||
void dhcp_devman_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type);
|
||||
|
||||
/**
|
||||
* @code dhcp_devman_active_types(dhcpv4, dhcpv6);
|
||||
*
|
||||
* @brief update local variables with active protocols
|
||||
*
|
||||
* @param dhcpv4 flag indicating dhcpv4 is enabled
|
||||
* @param dhcpv6 flag indicating dhcpv6 is enabled
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void dhcp_devman_active_types(bool dhcpv4, bool dhcpv6);
|
||||
|
||||
#endif /* DHCP_DEVMAN_H_ */
|
||||
|
@ -12,16 +12,24 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <syslog.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dhcp_mon.h"
|
||||
#include "dhcp_devman.h"
|
||||
|
||||
/** DHCP device/interface state counters */
|
||||
typedef struct
|
||||
{
|
||||
int v4_count; /** count in the number of DHCPv4 unhealthy checks */
|
||||
int v6_count; /** count in the number of DHCPv6 unhealthy checks */
|
||||
} dhcp_mon_count_t;
|
||||
|
||||
/** 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 */
|
||||
dhcp_mon_count_t counters; /** count in the number of unhealthy checks */
|
||||
const char *msg; /** message to be printed if unhealthy state is determined */
|
||||
} dhcp_mon_state_t;
|
||||
|
||||
@ -29,6 +37,10 @@ typedef struct
|
||||
static int window_interval_sec = 18;
|
||||
/** dhcp_unhealthy_max_count max count of consecutive unhealthy statuses before reporting to syslog */
|
||||
static int dhcp_unhealthy_max_count = 10;
|
||||
/** DHCP versions flags */
|
||||
static bool dhcpv4_enabled;
|
||||
static bool dhcpv6_enabled;
|
||||
|
||||
/** libevent base struct */
|
||||
static struct event_base *base;
|
||||
/** libevent timeout event struct */
|
||||
@ -45,13 +57,15 @@ static dhcp_mon_state_t state_data[] = {
|
||||
[0] = {
|
||||
.check_type = DHCP_MON_CHECK_POSITIVE,
|
||||
.get_context = dhcp_devman_get_agg_dev,
|
||||
.count = 0,
|
||||
.counters.v4_count = 0,
|
||||
.counters.v6_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,
|
||||
.counters.v4_count = 0,
|
||||
.counters.v6_count = 0,
|
||||
.msg = "dhcpmon detected DHCP packets traveling through mgmt interface (please check BGP routes.)"
|
||||
" Duration: %d (sec) for intf: '%s'\n"
|
||||
}
|
||||
@ -78,36 +92,89 @@ static void signal_callback(evutil_socket_t fd, short event, void *arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* @code check_dhcp_relay_health(state_data);
|
||||
* @code check_dhcp_relay_health(state_data, dhcp_type);
|
||||
*
|
||||
* @brief check DHCP relay overall health
|
||||
*
|
||||
* @param state_data pointer to dhcpmon state data
|
||||
*
|
||||
* @param type DHCP type
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void check_dhcp_relay_health(dhcp_mon_state_t *state_data)
|
||||
static void check_dhcp_relay_health(dhcp_mon_state_t *state_data, dhcp_type_t type)
|
||||
{
|
||||
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);
|
||||
dhcp_mon_status_t dhcp_mon_status = dhcp_devman_get_status(state_data->check_type, context, type);
|
||||
|
||||
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, DHCP_COUNTERS_SNAPSHOT);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_CURRENT);
|
||||
switch (type)
|
||||
{
|
||||
case DHCPv4_TYPE:
|
||||
if (++state_data->counters.v4_count > dhcp_unhealthy_max_count) {
|
||||
syslog(LOG_ALERT, state_data->msg, state_data->counters.v4_count * window_interval_sec, context->intf);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_SNAPSHOT);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_CURRENT);
|
||||
}
|
||||
break;
|
||||
case DHCPv6_TYPE:
|
||||
if (++state_data->counters.v6_count > dhcp_unhealthy_max_count) {
|
||||
syslog(LOG_ALERT, state_data->msg, state_data->counters.v6_count * window_interval_sec, context->intf);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_SNAPSHOT);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_CURRENT);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_ERR, "Unknown DHCP type %d\n", type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DHCP_MON_STATUS_HEALTHY:
|
||||
state_data->count = 0;
|
||||
break;
|
||||
case DHCP_MON_STATUS_INDETERMINATE:
|
||||
if (state_data->count) {
|
||||
state_data->count++;
|
||||
switch (type)
|
||||
{
|
||||
case DHCPv4_TYPE:
|
||||
state_data->counters.v4_count = 0;
|
||||
break;
|
||||
case DHCPv6_TYPE:
|
||||
state_data->counters.v6_count = 0;
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "Unknown DHCP type %d\n", type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DHCP_MON_STATUS_INDETERMINATE:
|
||||
switch (type)
|
||||
{
|
||||
case DHCPv4_TYPE:
|
||||
if (state_data->counters.v4_count) {
|
||||
if (++state_data->counters.v4_count > dhcp_unhealthy_max_count) {
|
||||
syslog(LOG_ALERT, state_data->msg, state_data->counters.v4_count * window_interval_sec, context->intf);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_SNAPSHOT);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_CURRENT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DHCPv6_TYPE:
|
||||
if (state_data->counters.v6_count) {
|
||||
if (++state_data->counters.v6_count > dhcp_unhealthy_max_count) {
|
||||
syslog(LOG_ALERT, state_data->msg, state_data->counters.v6_count * window_interval_sec, context->intf);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_SNAPSHOT);
|
||||
dhcp_devman_print_status(context, DHCP_COUNTERS_CURRENT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "Unknown DHCP type %d\n", type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_ERR, "DHCP Relay returned unknown status %d\n", dhcp_mon_status);
|
||||
break;
|
||||
@ -128,7 +195,12 @@ static void check_dhcp_relay_health(dhcp_mon_state_t *state_data)
|
||||
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++) {
|
||||
check_dhcp_relay_health(&state_data[i]);
|
||||
if (dhcpv4_enabled) {
|
||||
check_dhcp_relay_health(&state_data[i], DHCPv4_TYPE);
|
||||
}
|
||||
if (dhcpv6_enabled) {
|
||||
check_dhcp_relay_health(&state_data[i], DHCPv6_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_devman_update_snapshot(NULL);
|
||||
@ -141,13 +213,17 @@ static void timeout_callback(evutil_socket_t fd, short event, void *arg)
|
||||
* seconds. It also writes to syslog when dhcp relay has been unhealthy for consecutive max_count checks.
|
||||
*
|
||||
*/
|
||||
int dhcp_mon_init(int window_sec, int max_count)
|
||||
int dhcp_mon_init(int window_sec, int max_count, bool dhcpv4, bool dhcpv6)
|
||||
{
|
||||
int rv = -1;
|
||||
|
||||
do {
|
||||
window_interval_sec = window_sec;
|
||||
dhcp_unhealthy_max_count = max_count;
|
||||
dhcpv4_enabled = dhcpv4;
|
||||
dhcpv6_enabled = dhcpv6;
|
||||
|
||||
dhcp_devman_active_types(dhcpv4, dhcpv6);
|
||||
|
||||
base = event_base_new();
|
||||
if (base == NULL) {
|
||||
|
@ -17,10 +17,12 @@
|
||||
*
|
||||
* @param window_sec time interval between health checks
|
||||
* @param max_count max count of consecutive unhealthy statuses before reporting to syslog
|
||||
* @param dhcpv4 flag indicating dhcpv4 is enabled
|
||||
* @param dhcpv6 flag indicating dhcpv6 is enabled
|
||||
*
|
||||
* @return 0 upon success, otherwise upon failure
|
||||
*/
|
||||
int dhcp_mon_init(int window_sec, int max_count);
|
||||
int dhcp_mon_init(int window_sec, int max_count, bool dhcpv4, bool dhcpv6);
|
||||
|
||||
/**
|
||||
* @code dhcp_mon_shutdown();
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dhcp_mon.h"
|
||||
#include "dhcp_devman.h"
|
||||
@ -41,7 +42,8 @@ static const uint32_t dhcpmon_default_unhealthy_max_count = 10;
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
printf("Usage: %s -id <south interface> {-iu <north interface>}+ -im <mgmt interface> [-u <loopback interface>]"
|
||||
"[-w <snapshot window in sec>] [-c <unhealthy status count>] [-s <snap length>] [-d]\n", prog);
|
||||
"[-w <snapshot window in sec>] [-c <unhealthy status count>] [-s <snap length>]"
|
||||
"[-4 <DHCPv4 monitoring>] [-6 <DHCPv4 monitoring>] [-d]\n", prog);
|
||||
printf("where\n");
|
||||
printf("\tsouth interface: is a vlan interface,\n");
|
||||
printf("\tnorth interface: is a TOR-T1 interface,\n");
|
||||
@ -112,6 +114,8 @@ int main(int argc, char **argv)
|
||||
int max_unhealthy_count = dhcpmon_default_unhealthy_max_count;
|
||||
size_t snaplen = dhcpmon_default_snaplen;
|
||||
int make_daemon = 0;
|
||||
bool dhcpv4_enabled = false;
|
||||
bool dhcpv6_enabled = false;
|
||||
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
openlog(basename(argv[0]), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
||||
@ -155,6 +159,14 @@ int main(int argc, char **argv)
|
||||
max_unhealthy_count = atoi(argv[i + 1]);
|
||||
i += 2;
|
||||
break;
|
||||
case '4':
|
||||
dhcpv4_enabled = true;
|
||||
i++;
|
||||
break;
|
||||
case '6':
|
||||
dhcpv6_enabled = true;
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: %c: Unknown option\n", basename(argv[0]), argv[i][1]);
|
||||
usage(basename(argv[0]));
|
||||
@ -165,7 +177,11 @@ int main(int argc, char **argv)
|
||||
dhcpmon_daemonize();
|
||||
}
|
||||
|
||||
if ((dhcp_mon_init(window_interval, max_unhealthy_count) == 0) &&
|
||||
if (!dhcpv4_enabled && !dhcpv6_enabled) {
|
||||
dhcpv4_enabled = true;
|
||||
}
|
||||
|
||||
if ((dhcp_mon_init(window_interval, max_unhealthy_count, dhcpv4_enabled, dhcpv6_enabled) == 0) &&
|
||||
(dhcp_mon_start(snaplen) == 0)) {
|
||||
|
||||
rv = EXIT_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user