Add basic EOAM CFM CC Support (experimental)

This commit is contained in:
Christian Giese
2021-06-16 15:26:32 +02:00
parent 32298fded2
commit 656e5a75db
10 changed files with 341 additions and 45 deletions
+4 -2
View File
@@ -187,8 +187,10 @@ Attribute | Description | Default
`dhcp` | Optionally enable/disable DHCP per access configuration
`dhcpv6` | Optionally enable/disable DHCPv6 per access configuration
`stream-group-id` | Optional stream group identifier
**WARNING**: DHCP (IPv4) is currently not supported!
`cfm-cc` | Optionally enable/disable EOAM CFM CC (IPoE only) | false
`cfm-level` | Set EOAM CFM maintenance domain level | 0
`cfm-ma-id` | Set EOAM CFM maintenance association identifier | 0
`cfm-ma-name` | Set EOAM CFM maintenance association short name
For all modes it is possible to configure between zero and three VLAN
tags on the access interface as shown below.
+11 -3
View File
@@ -127,15 +127,19 @@ Attribute | Description
`interfaces` | List all interfaces with index
`session-counters` | Return session counters
`terminate` | Terminate all sessions similar to sending SIGINT (ctr+c)
`session-traffic` | Display session traffic statistics | |
`session-traffic` | Display session traffic statistics
`session-traffic-enabled` | Enable session traffic for all sessions
`session-traffic-disabled` | Disable session traffic for all sessions
`stream-traffic-enabled` | Enable stream traffic for all sessions | |
`stream-traffic-disabled` | Disable stream traffic for all sessions | |
`stream-traffic-enabled` | Enable stream traffic for all sessions
`stream-traffic-disabled` | Disable stream traffic for all sessions
`multicast-traffic-start` | Start sending multicast traffic from network interface
`multicast-traffic-stop` | Stop sending multicast traffic from network interface
`li-flows` | List all LI flows with detailed statistics
`sessions-pending` | List all sessions not established
`cfm-cc-start` | Start EOAM CFM CC
`cfm-cc-stop` | Stop EOAM CFM CC
`cfm-cc-rdi-on` | Set EOAM CFM CC RDI
`cfm-cc-rdi-off` | Unset EOAM CFM CC RDI
### Session Commands
@@ -174,6 +178,10 @@ Attribute | Description | Mandatory Arguments | Optional Arguments
`igmp-join` | Join group | `group` | `source1`, `source2`, `source3`
`igmp-leave` | Leave group | `group` |
`igmp-info` | IGMP information | |
`cfm-cc-start` | Start EOAM CFM CC
`cfm-cc-stop` | Stop EOAM CFM CC
`cfm-cc-rdi-on` | Set EOAM CFM CC RDI
`cfm-cc-rdi-off` | Unset EOAM CFM CC RDI
The `session-id` is the same as used for `{session-global}` in the
configuration section. This number starts with 1 and is increased
+58 -40
View File
@@ -81,6 +81,7 @@
#define BBL_SEND_ICMPV6_REPLY 0x00080000
#define BBL_SEND_ICMPV6_NS 0x00100000
#define BBL_SEND_ICMPV6_NA 0x00200000
#define BBL_SEND_CFM_CC 0x00400000
/* Network Interface */
#define BBL_IF_SEND_ARP_REQUEST 0x00000001
@@ -252,6 +253,8 @@ typedef struct bbl_interface_
/* Packet Stats */
uint32_t arp_tx;
uint32_t arp_rx;
uint32_t cfm_cc_tx;
uint32_t cfm_cc_rx;
uint32_t padi_tx;
uint32_t pado_rx;
uint32_t padr_tx;
@@ -342,55 +345,61 @@ typedef struct bbl_interface_
typedef struct bbl_access_config_
{
bool exhausted;
uint32_t sessions; /* per access config session counter */
struct bbl_interface_ *access_if;
bool exhausted;
uint32_t sessions; /* per access config session counter */
struct bbl_interface_ *access_if;
char *interface;
char *interface;
bbl_access_type_t access_type; /* pppoe or ipoe */
bbl_vlan_mode_t vlan_mode; /* 1:1 (default) or N:1 */
bbl_access_type_t access_type; /* pppoe or ipoe */
bbl_vlan_mode_t vlan_mode; /* 1:1 (default) or N:1 */
uint16_t stream_group_id;
uint16_t stream_group_id;
uint16_t access_outer_vlan;
uint16_t access_outer_vlan_min;
uint16_t access_outer_vlan_max;
uint16_t access_inner_vlan;
uint16_t access_inner_vlan_min;
uint16_t access_inner_vlan_max;
uint16_t access_third_vlan;
uint16_t access_outer_vlan;
uint16_t access_outer_vlan_min;
uint16_t access_outer_vlan_max;
uint16_t access_inner_vlan;
uint16_t access_inner_vlan_min;
uint16_t access_inner_vlan_max;
uint16_t access_third_vlan;
/* Static */
uint32_t static_ip;
uint32_t static_ip_iter;
uint32_t static_gateway;
uint32_t static_gateway_iter;
/* Static */
uint32_t static_ip;
uint32_t static_ip_iter;
uint32_t static_gateway;
uint32_t static_gateway_iter;
/* Authentication */
char *username;
char *password;
uint16_t authentication_protocol;
/* Authentication */
char *username;
char *password;
uint16_t authentication_protocol;
/* Access Line */
char *agent_remote_id;
char *agent_circuit_id;
uint32_t rate_up;
uint32_t rate_down;
uint32_t dsl_type;
/* Access Line */
char *agent_remote_id;
char *agent_circuit_id;
uint32_t rate_up;
uint32_t rate_down;
uint32_t dsl_type;
/* Protocols */
bool ipcp_enable;
bool ip6cp_enable;
bool ipv4_enable;
bool ipv6_enable;
bool dhcp_enable;
bool dhcpv6_enable;
bool igmp_autostart;
uint8_t igmp_version;
bool session_traffic_autostart;
/* Protocols */
bool ipcp_enable;
bool ip6cp_enable;
bool ipv4_enable;
bool ipv6_enable;
bool dhcp_enable;
bool dhcpv6_enable;
bool igmp_autostart;
uint8_t igmp_version;
bool session_traffic_autostart;
void *next; /* pointer to next access config element */
/* CFM CC */
bool cfm_cc;
uint8_t cfm_level;
uint16_t cfm_ma_id;
char *cfm_ma_name;
void *next; /* pointer to next access config element */
} bbl_access_config_s;
/*
@@ -745,6 +754,7 @@ typedef struct bbl_session_
struct timer_ *timer_session_traffic_ipv6;
struct timer_ *timer_session_traffic_ipv6pd;
struct timer_ *timer_rate;
struct timer_ *timer_cfm_cc;
bbl_access_type_t access_type;
@@ -782,6 +792,14 @@ typedef struct bbl_session_
uint8_t server_mac[ETH_ADDR_LEN];
uint8_t client_mac[ETH_ADDR_LEN];
/* CFM */
bool cfm_cc;
bool cfm_rdi;
uint32_t cfm_seq;
uint8_t cfm_level;
uint16_t cfm_ma_id;
char *cfm_ma_name;
/* PPPoE */
uint16_t pppoe_session_id;
uint8_t *pppoe_ac_cookie;
+24
View File
@@ -321,6 +321,30 @@ json_parse_access_interface (bbl_ctx_s *ctx, json_t *access_interface, bbl_acces
if (value) {
access_config->stream_group_id = json_number_value(value);
}
value = json_object_get(access_interface, "cfm-cc");
if (json_is_boolean(value)) {
access_config->cfm_cc = json_boolean_value(value);
}
value = json_object_get(access_interface, "cfm-level");
if (value) {
access_config->cfm_level = json_number_value(value);
if(access_config->cfm_level > 7) {
fprintf(stderr, "JSON config error: Invalid value for access->cfm-level\n");
return false;
}
}
value = json_object_get(access_interface, "cfm-ma-id");
if (value) {
access_config->cfm_ma_id = json_number_value(value);
}
if (json_unpack(access_interface, "{s:s}", "cfm-ma-name", &s) == 0) {
access_config->cfm_ma_name = strdup(s);
} else if (access_config->cfm_cc) {
fprintf(stderr, "JSON config error: Missing access->cfm-ma-name\n");
return false;
}
return true;
}
+73
View File
@@ -1208,6 +1208,75 @@ bbl_ctrl_sessions_pending(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribut
return result;
}
ssize_t
bbl_ctrl_cfm_cc_start_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool status) {
bbl_session_s *session;
uint32_t i;
if(session_id) {
session = bbl_session_get(ctx, session_id);
if(session) {
session->cfm_cc = status;
return bbl_ctrl_status(fd, "ok", 200, NULL);
} else {
return bbl_ctrl_status(fd, "warning", 404, "session not found");
}
} else {
/* Iterate over all sessions */
for(i = 0; i < ctx->sessions; i++) {
session = ctx->session_list[i];
if(session) {
session->cfm_cc = status;
}
}
return bbl_ctrl_status(fd, "ok", 200, NULL);
}
}
ssize_t
bbl_ctrl_cfm_cc_start(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
return bbl_ctrl_cfm_cc_start_stop(fd, ctx, session_id, true);
}
ssize_t
bbl_ctrl_cfm_cc_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
return bbl_ctrl_cfm_cc_start_stop(fd, ctx, session_id, false);
}
ssize_t
bbl_ctrl_cfm_cc_rdi(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool status) {
bbl_session_s *session;
uint32_t i;
if(session_id) {
session = bbl_session_get(ctx, session_id);
if(session) {
session->cfm_rdi = status;
return bbl_ctrl_status(fd, "ok", 200, NULL);
} else {
return bbl_ctrl_status(fd, "warning", 404, "session not found");
}
} else {
/* Iterate over all sessions */
for(i = 0; i < ctx->sessions; i++) {
session = ctx->session_list[i];
if(session) {
session->cfm_rdi = status;
}
}
return bbl_ctrl_status(fd, "ok", 200, NULL);
}
}
ssize_t
bbl_ctrl_cfm_cc_rdi_on(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
return bbl_ctrl_cfm_cc_rdi(fd, ctx, session_id, true);
}
ssize_t
bbl_ctrl_cfm_cc_rdi_off(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
return bbl_ctrl_cfm_cc_rdi(fd, ctx, session_id, false);
}
struct action {
char *name;
callback_function *fn;
@@ -1242,6 +1311,10 @@ struct action actions[] = {
{"stream-traffic-disabled", bbl_ctrl_stream_traffic_stop},
{"stream-traffic-stop", bbl_ctrl_stream_traffic_stop},
{"sessions-pending", bbl_ctrl_sessions_pending},
{"cfm-cc-start", bbl_ctrl_cfm_cc_start},
{"cfm-cc-stop", bbl_ctrl_cfm_cc_stop},
{"cfm-cc-rdi-on", bbl_ctrl_cfm_cc_rdi_on},
{"cfm-cc-rdi-off", bbl_ctrl_cfm_cc_rdi_off},
{NULL, NULL},
};
+97
View File
@@ -1423,6 +1423,101 @@ encode_pppoe_session(uint8_t *buf, uint16_t *len,
return result;
}
/*
* encode_cfm
*/
protocol_error_t
encode_cfm(uint8_t *buf, uint16_t *len, bbl_cfm_t *cfm) {
uint8_t max_ma_str_len = 45;
if(cfm->type != CFM_TYPE_CCM) {
/* Currently only CFM CC is supported */
return ENCODE_ERROR;
}
*buf = cfm->md_level << 5; /* CFM MD level and version */
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
*buf = cfm->type; /* CFM OpCode */
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
/* Set CFM CC interval fixed to 1s (4) */
*buf = 4;
if(cfm->rdi) {
/* Set RDI bit */
*buf |= 128;
}
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
/* Remember first TLV offset position */
*buf = 70;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
memset(buf, 0x0, 70);
/* Sequence Number */
*(uint32_t*)buf = htobe32(cfm->seq);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t));
/* MA Identifier */
*(uint16_t*)buf = htobe16(cfm->ma_id);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
/* MD Name */
*buf = cfm->md_name_format;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
if(cfm->md_name_format != CMF_MD_NAME_FORMAT_NONE) {
if(cfm->md_name_len > 32) cfm->md_name_len = 32;
*buf = cfm->md_name_len;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
memcpy(buf, cfm->md_name, cfm->md_name_len);
BUMP_WRITE_BUFFER(buf, len, cfm->md_name_len);
max_ma_str_len -= cfm->md_name_len + 1;
}
/* MA Name */
*buf = cfm->ma_name_format;
if(cfm->ma_name_len > max_ma_str_len) cfm->ma_name_len = max_ma_str_len;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
*buf = cfm->ma_name_len;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
memcpy(buf, cfm->ma_name, cfm->ma_name_len);
BUMP_WRITE_BUFFER(buf, len, max_ma_str_len);
/* Y.1731 */
BUMP_WRITE_BUFFER(buf, len, 16);
/* CFM TLVs */
/* Sender ID TLV */
*buf = 1;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
*(uint16_t*)buf = htobe16(1);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*buf = 0;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
/* Port Status TLV */
*buf = 2;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
*(uint16_t*)buf = htobe16(1);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*buf = 2; /* UP */
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
/* Interface Status TLV */
*buf = 4;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
*(uint16_t*)buf = htobe16(1);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*buf = 1; /* UP */
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
/* End TLV */
*buf = 0;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t));
return PROTOCOL_SUCCESS;
}
/*
* encode_ethernet
*/
@@ -1478,6 +1573,8 @@ encode_ethernet(uint8_t *buf, uint16_t *len,
return encode_ipv4(buf, len, (bbl_ipv4_t*)eth->next);
case ETH_TYPE_IPV6:
return encode_ipv6(buf, len, (bbl_ipv6_t*)eth->next);
case ETH_TYPE_CFM:
return encode_cfm(buf, len, (bbl_cfm_t*)eth->next);
default:
return UNKNOWN_PROTOCOL;
}
+20
View File
@@ -42,6 +42,7 @@
#define ETH_TYPE_ARP 0x0806
#define ETH_TYPE_IPV4 0x0800
#define ETH_TYPE_IPV6 0x86dd
#define ETH_TYPE_CFM 0x8902
#define ETH_ADDR_LEN 6
#define ETH_VLAN_ID_MAX 4095
@@ -204,6 +205,11 @@
#define QMX_LI_UDP_PORT 49152
#define CFM_TYPE_CCM 1
#define CMF_MD_NAME_FORMAT_NONE 1
#define CMF_MD_NAME_FORMAT_STRING 4
#define CMF_MA_NAME_FORMAT_STRING 2
#define MAX_VLANS 3
#define BUMP_BUFFER(_buf, _len, _size) \
@@ -798,6 +804,20 @@ typedef struct bbl_qmx_li_ {
uint16_t payload_len; // LI payload length
} bbl_qmx_li_t;
typedef struct bbl_cfm_ {
uint8_t type;
uint32_t seq;
bool rdi;
uint8_t md_level;
uint8_t md_name_format;
uint8_t md_name_len;
uint8_t *md_name;
uint16_t ma_id;
uint8_t ma_name_format;
uint8_t ma_name_len;
uint8_t *ma_name;
} bbl_cfm_t;
/*
* decode_ethernet
*/
+13
View File
@@ -389,6 +389,15 @@ bbl_rx_udp_ipv6(bbl_ethernet_header_t *eth, bbl_ipv6_t *ipv6, bbl_interface_s *i
}
}
void
bbl_cfm_cc(timer_s *timer) {
bbl_session_s *session = timer->data;
if(session->session_state == BBL_ESTABLISHED && session->cfm_cc) {
session->send_requests |= BBL_SEND_CFM_CC;
bbl_session_network_tx_qnode_insert(session);
}
}
void
bbl_rx_established_ipoe(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_s *session) {
@@ -428,6 +437,10 @@ bbl_rx_established_ipoe(bbl_ethernet_header_t *eth, bbl_interface_s *interface,
bbl_session_traffic_start_ipv6(ctx, session);
bbl_session_traffic_start_ipv6pd(ctx, session);
}
if(session->cfm_cc) {
/* Start CFM CC (currently fixed set to 1s) */
timer_add_periodic(&ctx->timer_root, &session->timer_cfm_cc, "CFM-CC", 1, 0, session, &bbl_cfm_cc);
}
}
}
}
+13
View File
@@ -473,6 +473,19 @@ bbl_sessions_init(bbl_ctx_s *ctx)
session->agent_remote_id = strdup(s);
}
/* Update CFM */
if(access_config->cfm_cc) {
session->cfm_cc = true;
session->cfm_level = access_config->cfm_level;
session->cfm_ma_id = access_config->cfm_ma_id;
if(access_config->cfm_ma_name) {
s = replace_substring(access_config->cfm_ma_name, "{session-global}", snum1);
session->cfm_ma_name = s;
s = replace_substring(session->cfm_ma_name, "{session}", snum2);
session->cfm_ma_name = strdup(s);
}
}
/* Update access rates ... */
session->rate_up = access_config->rate_up;
session->rate_down = access_config->rate_down;
+28
View File
@@ -1488,6 +1488,31 @@ bbl_encode_packet_arp_reply (bbl_session_s *session)
return encode_ethernet(session->write_buf, &session->write_idx, &eth);
}
protocol_error_t
bbl_encode_packet_cfm_cc (bbl_session_s *session)
{
bbl_ethernet_header_t eth = {0};
bbl_cfm_t cfm = {0};
eth.dst = session->server_mac;
eth.src = session->client_mac;
eth.vlan_outer = session->vlan_key.outer_vlan_id;
eth.vlan_inner = session->vlan_key.inner_vlan_id;
eth.vlan_three = session->access_third_vlan;
eth.type = ETH_TYPE_CFM;
eth.next = &cfm;
cfm.type = CFM_TYPE_CCM;
cfm.seq = session->cfm_seq++;
cfm.rdi = session->cfm_rdi;
cfm.md_level = session->cfm_level;
cfm.md_name_format = CMF_MD_NAME_FORMAT_NONE;
cfm.ma_id = session->cfm_ma_id;
cfm.ma_name_format = CMF_MA_NAME_FORMAT_STRING;
cfm.ma_name_len = strlen(session->cfm_ma_name);
cfm.ma_name = (uint8_t*)session->cfm_ma_name;
session->interface->stats.cfm_cc_tx++;
return encode_ethernet(session->write_buf, &session->write_idx, &eth);
}
protocol_error_t
bbl_encode_packet (bbl_session_s *session, uint8_t *buf, uint16_t *len, bool *accounting)
{
@@ -1561,6 +1586,9 @@ bbl_encode_packet (bbl_session_s *session, uint8_t *buf, uint16_t *len, bool *ac
} else if (session->send_requests & BBL_SEND_DHCP_REQUEST) {
result = bbl_encode_packet_dhcp(session);
session->send_requests &= ~BBL_SEND_DHCP_REQUEST;
} else if (session->send_requests & BBL_SEND_CFM_CC) {
result = bbl_encode_packet_cfm_cc(session);
session->send_requests &= ~BBL_SEND_CFM_CC;
} else {
session->send_requests = 0;
}