218 lines
5.1 KiB
C
218 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/debugfs.h>
|
|
#include "mtk_eth_soc.h"
|
|
|
|
struct mtk_flow_addr_info
|
|
{
|
|
void *src, *dest;
|
|
u16 *src_port, *dest_port;
|
|
bool ipv6;
|
|
};
|
|
|
|
static const char *mtk_foe_entry_state_str(int state)
|
|
{
|
|
static const char * const state_str[] = {
|
|
[MTK_FOE_STATE_INVALID] = "INV",
|
|
[MTK_FOE_STATE_UNBIND] = "UNB",
|
|
[MTK_FOE_STATE_BIND] = "BND",
|
|
[MTK_FOE_STATE_FIN] = "FIN",
|
|
};
|
|
|
|
if (state >= ARRAY_SIZE(state_str) || !state_str[state])
|
|
return "UNK";
|
|
|
|
return state_str[state];
|
|
}
|
|
|
|
static const char *mtk_foe_pkt_type_str(int type)
|
|
{
|
|
static const char * const type_str[] = {
|
|
[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
|
|
[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
|
|
[MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
|
|
[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
|
|
[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
|
|
[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
|
|
[MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
|
|
};
|
|
|
|
if (type >= ARRAY_SIZE(type_str) || !type_str[type])
|
|
return "UNKNOWN";
|
|
|
|
return type_str[type];
|
|
}
|
|
|
|
static void
|
|
mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
|
|
{
|
|
u32 n_addr[4];
|
|
int i;
|
|
|
|
if (!ipv6) {
|
|
seq_printf(m, "%pI4h", addr);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(n_addr); i++)
|
|
n_addr[i] = htonl(addr[i]);
|
|
seq_printf(m, "%pI6", n_addr);
|
|
}
|
|
|
|
static void
|
|
mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
|
|
{
|
|
mtk_print_addr(m, ai->src, ai->ipv6);
|
|
if (ai->src_port)
|
|
seq_printf(m, ":%d", *ai->src_port);
|
|
seq_printf(m, "->");
|
|
mtk_print_addr(m, ai->dest, ai->ipv6);
|
|
if (ai->dest_port)
|
|
seq_printf(m, ":%d", *ai->dest_port);
|
|
}
|
|
|
|
static int
|
|
mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
|
|
{
|
|
struct mtk_ppe *ppe = m->private;
|
|
int i;
|
|
|
|
for (i = 0; i < MTK_PPE_ENTRIES; i++) {
|
|
struct mtk_foe_entry *entry = &ppe->foe_table[i];
|
|
struct mtk_foe_mac_info *l2;
|
|
struct mtk_flow_addr_info ai = {};
|
|
unsigned char h_source[ETH_ALEN];
|
|
unsigned char h_dest[ETH_ALEN];
|
|
int type, state;
|
|
u32 ib2;
|
|
|
|
|
|
state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
|
|
if (!state)
|
|
continue;
|
|
|
|
if (bind && state != MTK_FOE_STATE_BIND)
|
|
continue;
|
|
|
|
type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
|
|
seq_printf(m, "%05x %s %7s", i,
|
|
mtk_foe_entry_state_str(state),
|
|
mtk_foe_pkt_type_str(type));
|
|
|
|
switch (type) {
|
|
case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
|
|
case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
|
|
ai.src_port = &entry->ipv4.orig.src_port;
|
|
ai.dest_port = &entry->ipv4.orig.dest_port;
|
|
fallthrough;
|
|
case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
|
|
ai.src = &entry->ipv4.orig.src_ip;
|
|
ai.dest = &entry->ipv4.orig.dest_ip;
|
|
break;
|
|
case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
|
|
ai.src_port = &entry->ipv6.src_port;
|
|
ai.dest_port = &entry->ipv6.dest_port;
|
|
fallthrough;
|
|
case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
|
|
case MTK_PPE_PKT_TYPE_IPV6_6RD:
|
|
ai.src = &entry->ipv6.src_ip;
|
|
ai.dest = &entry->ipv6.dest_ip;
|
|
ai.ipv6 = true;
|
|
break;
|
|
}
|
|
|
|
seq_printf(m, " orig=");
|
|
mtk_print_addr_info(m, &ai);
|
|
|
|
switch (type) {
|
|
case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
|
|
case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
|
|
ai.src_port = &entry->ipv4.new.src_port;
|
|
ai.dest_port = &entry->ipv4.new.dest_port;
|
|
fallthrough;
|
|
case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
|
|
ai.src = &entry->ipv4.new.src_ip;
|
|
ai.dest = &entry->ipv4.new.dest_ip;
|
|
seq_printf(m, " new=");
|
|
mtk_print_addr_info(m, &ai);
|
|
break;
|
|
}
|
|
|
|
if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
|
|
l2 = &entry->ipv6.l2;
|
|
ib2 = entry->ipv6.ib2;
|
|
} else {
|
|
l2 = &entry->ipv4.l2;
|
|
ib2 = entry->ipv4.ib2;
|
|
}
|
|
|
|
*((__be32 *)h_source) = htonl(l2->src_mac_hi);
|
|
*((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
|
|
*((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
|
|
*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
|
|
|
|
seq_printf(m, " eth=%pM->%pM etype=%04x"
|
|
" vlan=%d,%d ib1=%08x ib2=%08x\n",
|
|
h_source, h_dest, ntohs(l2->etype),
|
|
l2->vlan1, l2->vlan2, entry->ib1, ib2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private)
|
|
{
|
|
return mtk_ppe_debugfs_foe_show(m, private, false);
|
|
}
|
|
|
|
static int
|
|
mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private)
|
|
{
|
|
return mtk_ppe_debugfs_foe_show(m, private, true);
|
|
}
|
|
|
|
static int
|
|
mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, mtk_ppe_debugfs_foe_show_all,
|
|
inode->i_private);
|
|
}
|
|
|
|
static int
|
|
mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, mtk_ppe_debugfs_foe_show_bind,
|
|
inode->i_private);
|
|
}
|
|
|
|
int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
|
|
{
|
|
static const struct file_operations fops_all = {
|
|
.open = mtk_ppe_debugfs_foe_open_all,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static const struct file_operations fops_bind = {
|
|
.open = mtk_ppe_debugfs_foe_open_bind,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
struct dentry *root;
|
|
|
|
root = debugfs_create_dir("mtk_ppe", NULL);
|
|
if (!root)
|
|
return -ENOMEM;
|
|
|
|
debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all);
|
|
debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind);
|
|
|
|
return 0;
|
|
}
|