mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
5.1 KiB
217 lines
5.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* Copyright (C) 2020 Felix Fietkau <[email protected]> */ |
|
|
|
#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; |
|
}
|
|
|