Skip to content

Commit

Permalink
pppoe: Add support to VRRP traffic monitoring
Browse files Browse the repository at this point in the history
This feature will monitor VRRP traffic on PPPoE interface and will force
transition into FAULT state when no VRRP adverts are received during
configured expiration timeout.
  • Loading branch information
acassen committed Apr 7, 2024
1 parent 1eea97c commit d13027e
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 10 deletions.
2 changes: 1 addition & 1 deletion lib/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ typedef enum _node_type {
CFG_LOG_NODE, /* Configure the logging */

PDN_NODE, /* PDN daemon commands. */
IP_VRF_NODE, /* IP VRF commands. */
PPPOE_NODE, /* PPPoE commands. */
PPPOE_BUNDLE_NODE, /* PPPoE Bundle commands. */
IP_VRF_NODE, /* IP VRF commands. */
APN_NODE, /* APN commands. */
GTP_SWITCH_NODE, /* GTP Switch commands. */
GTP_ROUTER_NODE, /* GTP Router commands. */
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ OBJS = main.o gtp_data.o gtp_vty.o gtp_if.o gtp_server.o \
gtp_switch.o gtp_switch_vty.o gtp_switch_hdl.o gtp_switch_hdl_v1.o \
gtp_switch_hdl_v2.o gtp_router.o gtp_router_vty.o gtp_router_hdl.o \
gtp_pppoe.o gtp_pppoe_session.o gtp_pppoe_proto.o gtp_pppoe_vty.o \
gtp_ppp.o
gtp_pppoe_monitor.o gtp_ppp.o

HEADERS = $(OBJS:.o=.h)

Expand Down
10 changes: 5 additions & 5 deletions src/gtp_dpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ gtp_dpd_read_thread(thread_ref_t thread)
static int
gtp_dpd_ingress_socket_init(gtp_iptnl_t *t)
{
int fd, ret;
int fd, err;
struct sock_filter bpfcode[18] = {
{ 0x30, 0, 0, 0x00000000 },
{ 0x54, 0, 0, 0x000000f0 },
Expand Down Expand Up @@ -354,8 +354,8 @@ gtp_dpd_ingress_socket_init(gtp_iptnl_t *t)
if (fd < 0)
return -1;

ret = bind(fd, (struct sockaddr *) &sll, sizeof(struct sockaddr_ll));
if (ret < 0) {
err = bind(fd, (struct sockaddr *) &sll, sizeof(struct sockaddr_ll));
if (err) {
log_message(LOG_INFO, "%s(): failed binding to ifindex:%d. (%m)"
, __FUNCTION__
, t->ifindex);
Expand All @@ -368,8 +368,8 @@ gtp_dpd_ingress_socket_init(gtp_iptnl_t *t)
bpfcode[6].k = 0x8badf00d;

/* Attach filter */
ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (ret < 0) {
err = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (err) {
log_message(LOG_INFO, "%s(): failed to attach filter. (%m)"
, __FUNCTION__);
close(fd);
Expand Down
1 change: 1 addition & 0 deletions src/gtp_pppoe.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ __gtp_pppoe_release(gtp_pppoe_t *pppoe)
gtp_htab_destroy(&pppoe->session_tab);
gtp_htab_destroy(&pppoe->unique_tab);
pkt_queue_destroy(&pppoe->pkt_q);
gtp_pppoe_monitor_vrrp_destroy(pppoe);
FREE(pppoe);
return 0;
}
Expand Down
184 changes: 184 additions & 0 deletions src/gtp_pppoe_monitor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
/*
* Soft: The main goal of gtp-guard is to provide robust and secure
* extensions to GTP protocol (GPRS Tunneling Procol). GTP is
* widely used for data-plane in mobile core-network. gtp-guard
* implements a set of 3 main frameworks:
* A Proxy feature for data-plane tweaking, a Routing facility
* to inter-connect and a Firewall feature for filtering,
* rewriting and redirecting.
*
* Authors: Alexandre Cassen, <acassen@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public
* License Version 3.0 as published by the Free Software Foundation;
* either version 3.0 of the License, or (at your option) any later
* version.
*
* Copyright (C) 2023 Alexandre Cassen, <acassen@gmail.com>
*/

/* system includes */
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/filter.h>
#include <net/if.h>
#include <errno.h>

/* local includes */
#include "gtp_guard.h"


/*
* Monitoring thread
*/
static void
gtp_pppoe_vrrp_timer_thread(thread_ref_t thread)
{
gtp_pppoe_t *pppoe = THREAD_ARG(thread);

/* Timer fired ? */
if (__test_bit(PPPOE_FL_FAULT_BIT, &pppoe->flags) && (pppoe->expire > timer_long(time_now))) {
log_message(LOG_INFO, "%s(): PPPoE Instance %s back-in-business..."
, __FUNCTION__
, pppoe->name);
__clear_bit(PPPOE_FL_FAULT_BIT, &pppoe->flags);
goto end;
}

if (!__test_bit(PPPOE_FL_FAULT_BIT, &pppoe->flags) && (pppoe->expire < timer_long(time_now))) {
log_message(LOG_INFO, "%s(): PPPoE Instance %s is out-of-order..."
, __FUNCTION__
, pppoe->name);
__set_bit(PPPOE_FL_FAULT_BIT, &pppoe->flags);
}

end:
thread_add_timer(master, gtp_pppoe_vrrp_timer_thread, pppoe, TIMER_HZ);
}

static void
gtp_pppoe_vrrp_read_thread(thread_ref_t thread)
{
gtp_pppoe_t *pppoe = THREAD_ARG(thread);
ssize_t len;

/* Handle read timeout */
if (thread->type == THREAD_READ_TIMEOUT)
goto end;

len = read(pppoe->monitor_fd, pppoe->monitor_buffer, GTP_BUFFER_SIZE);
if (len < 0) {
log_message(LOG_INFO, "%s(): Error reading for vrrp monitor socket (%m)"
, __FUNCTION__);
goto end;
}

/* Update expiration */
pppoe->expire = timer_long(time_now) + pppoe->credit;

end:
pppoe->r_thread = thread_add_read(master, gtp_pppoe_vrrp_read_thread
, pppoe, pppoe->monitor_fd, TIMER_HZ, 0);
}


/*
* BPF VRRP Filtering
*
* ASM Code :
* (000) ldh [12]
* (001) jeq #0x800 jt 2 jf 5
* (002) ldb [23]
* (003) jeq #0x70 jt 4 jf 5
* (004) ret #262144
* (005) ret #0
*/
static int
gtp_pppoe_monitor_vrrp_socket_init(gtp_pppoe_t *pppoe)
{
int fd, err;
struct sock_filter bpfcode[6] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 1, 0x00000070 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 }
};
struct sock_fprog bpf = {
.len = ARRAY_SIZE(bpfcode),
.filter = bpfcode
};
struct sockaddr_ll sll = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_ALL),
.sll_ifindex = pppoe->ifindex,
.sll_hatype = 0,
.sll_pkttype = PACKET_HOST,
.sll_halen = 0,
};

fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htons(ETH_P_ALL));
if (fd < 0)
return -1;

err = bind(fd, (struct sockaddr *) &sll, sizeof(struct sockaddr_ll));
if (err) {
log_message(LOG_INFO, "%s(): failed binding to ifindex:%d. (%m)"
, __FUNCTION__
, pppoe->ifindex);
close(fd);
return -1;
}

/* Attach filter */
err = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (err) {
log_message(LOG_INFO, "%s(): failed to attach filter. (%m)"
, __FUNCTION__);
close(fd);
return -1;
}

return fd;
}

/*
* PPPoE Monitoring
*/
int
gtp_pppoe_monitor_vrrp_init(gtp_pppoe_t *pppoe)
{
pppoe->monitor_fd = gtp_pppoe_monitor_vrrp_socket_init(pppoe);
if (pppoe->monitor_fd < 0) {
log_message(LOG_INFO, "%s(): Error creating VRRP minitoring socket (%m)"
, __FUNCTION__);
return -1;
}

log_message(LOG_INFO, "%s(): Activating VRRP monitoring for PPPoE instance:%s"
, __FUNCTION__
, pppoe->ifname);

/* Scheduling submition */
pppoe->r_thread = thread_add_read(master, gtp_pppoe_vrrp_read_thread
, pppoe, pppoe->monitor_fd, TIMER_HZ, 0);
thread_add_timer(master, gtp_pppoe_vrrp_timer_thread, pppoe, pppoe->credit);
return 0;
}

int
gtp_pppoe_monitor_vrrp_destroy(gtp_pppoe_t *pppoe)
{
if (__test_bit(PPPOE_FL_VRRP_MONITOR_BIT, &pppoe->flags))
close(pppoe->monitor_fd);
return 0;
}
36 changes: 36 additions & 0 deletions src/gtp_pppoe_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,39 @@ DEFUN(pppoe_interface,
return CMD_SUCCESS;
}

DEFUN(pppoe_monitor_vrrp,
pppoe_monitor_vrrp_cmd,
"monitor-vrrp <3-15>",
"VRRP Traffic monitoring\n"
"Timeout to transit into FAULT state\n")
{
gtp_pppoe_t *pppoe = vty->index;
int credit, err;

if (argc < 1) {
vty_out(vty, "%% missing arguments%s", VTY_NEWLINE);
return CMD_WARNING;
}

/* Credit handling */
pppoe->credit = 3 * TIMER_HZ;
VTY_GET_INTEGER_RANGE("Timeout", credit, argv[0], 3, 15);
pppoe->credit = credit * TIMER_HZ;
pppoe->expire = timer_long(time_now) + pppoe->credit;

err = gtp_pppoe_monitor_vrrp_init(pppoe);
if (err) {
vty_out(vty, "%% Error VRRP Monitoring on interface:%s (%s)%s"
, pppoe->ifname
, strerror(errno)
, VTY_NEWLINE);
return CMD_WARNING;
}

__set_bit(PPPOE_FL_VRRP_MONITOR_BIT, &pppoe->flags);
return CMD_SUCCESS;
}

DEFUN(pppoe_ac_name,
pppoe_ac_name_cmd,
"access-concentrator-name STRING",
Expand Down Expand Up @@ -630,6 +663,8 @@ gtp_config_pppoe_write(vty_t *vty)
if (pppoe->thread_cnt != GTP_PPPOE_RPS_SIZE)
vty_out(vty, " rps-bits %d", __builtin_ctz(pppoe->thread_cnt));
vty_out(vty, "%s", VTY_NEWLINE);
if (__test_bit(PPPOE_FL_VRRP_MONITOR_BIT, &pppoe->flags))
vty_out(vty, " monitor-vrrp %ld%s", pppoe->credit / TIMER_HZ, VTY_NEWLINE);
if (pppoe->ac_name[0])
vty_out(vty, " access-concentrator-name %s%s", pppoe->ac_name, VTY_NEWLINE);
if (pppoe->service_name[0])
Expand Down Expand Up @@ -723,6 +758,7 @@ gtp_pppoe_vty_init(void)
install_element(CONFIG_NODE, &no_pppoe_cmd);

install_element(PPPOE_NODE, &pppoe_interface_cmd);
install_element(PPPOE_NODE, &pppoe_monitor_vrrp_cmd);
install_element(PPPOE_NODE, &pppoe_ac_name_cmd);
install_element(PPPOE_NODE, &pppoe_service_name_cmd);
install_element(PPPOE_NODE, &pppoe_vendor_specific_bbf_cmd);
Expand Down
6 changes: 3 additions & 3 deletions src/gtp_switch_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ DEFUN(gtpu_ipip_dead_peer_detection,
{
gtp_switch_t *ctx = vty->index;
gtp_iptnl_t *t = &ctx->iptnl;
int credit, ifindex, plen, ret;
int credit, ifindex, plen, err;

if (!__test_bit(GTP_FL_GTP_FORWARD_LOADED_BIT, &daemon_data->flags)) {
vty_out(vty, "%% eBPF GTP-FORWARD program not loaded!%s", VTY_NEWLINE);
Expand Down Expand Up @@ -335,8 +335,8 @@ DEFUN(gtpu_ipip_dead_peer_detection,
}
t->ifindex = ifindex;

ret = gtp_dpd_init(&ctx->iptnl);
if (ret < 0) {
err = gtp_dpd_init(&ctx->iptnl);
if (err) {
vty_out(vty, "%% Error starting Dead-Peer-Detection on interface %s (%s)%s"
, argv[1]
, strerror(errno)
Expand Down
1 change: 1 addition & 0 deletions src/include/gtp_guard.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "gtp_pppoe.h"
#include "gtp_pppoe_session.h"
#include "gtp_pppoe_proto.h"
#include "gtp_pppoe_monitor.h"
#include "gtp_pppoe_vty.h"
#include "gtp_ppp.h"
#include "gtp_ppp_session.h"
Expand Down
7 changes: 7 additions & 0 deletions src/include/gtp_pppoe.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ enum pppoe_flags {
PPPOE_FL_PRIMARY_BIT,
PPPOE_FL_SECONDARY_BIT,
PPPOE_FL_FAULT_BIT,
PPPOE_FL_VRRP_MONITOR_BIT,
PPPOE_FL_GTP_USERNAME_TEMPLATE_0_BIT,
PPPOE_FL_GTP_USERNAME_TEMPLATE_1_BIT,
PPPOE_FL_VENDOR_SPECIFIC_BBF_BIT,
Expand Down Expand Up @@ -197,6 +198,12 @@ typedef struct _gtp_pppoe {
gtp_pppoe_worker_t *worker_ses;
pkt_queue_t pkt_q;

int monitor_fd; /* Monitoring channel */
unsigned char monitor_buffer[GTP_BUFFER_SIZE];
unsigned long credit;
unsigned long expire;
thread_ref_t r_thread;

list_head_t next;

unsigned long flags;
Expand Down
29 changes: 29 additions & 0 deletions src/include/gtp_pppoe_monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
/*
* Soft: The main goal of gtp-guard is to provide robust and secure
* extensions to GTP protocol (GPRS Tunneling Procol). GTP is
* widely used for data-plane in mobile core-network. gtp-guard
* implements a set of 3 main frameworks:
* A Proxy feature for data-plane tweaking, a Routing facility
* to inter-connect and a Firewall feature for filtering,
* rewriting and redirecting.
*
* Authors: Alexandre Cassen, <acassen@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public
* License Version 3.0 as published by the Free Software Foundation;
* either version 3.0 of the License, or (at your option) any later
* version.
*
* Copyright (C) 2023 Alexandre Cassen, <acassen@gmail.com>
*/

#ifndef _GTP_PPPOE_MONITOR_H
#define _GTP_PPPOE_MONITOR_H

/* Prototypes */
extern int gtp_pppoe_monitor_vrrp_init(gtp_pppoe_t *);
extern int gtp_pppoe_monitor_vrrp_destroy(gtp_pppoe_t *);

#endif

0 comments on commit d13027e

Please sign in to comment.