fix(firewall): support legacy ip RA checks and link documentation (#14083)

This commit is contained in:
ssongliu
2026-10-14 15:57:07 +08:00
committed by GitHub
parent f37afb1a37
commit 547f9775b0
3 changed files with 56 additions and 23 deletions
+36 -22
View File
@@ -2,7 +2,6 @@ package forwarding
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
@@ -174,14 +173,19 @@ func LoadDockerIPv4BridgePorts() (map[string]bool, error) {
if err != nil {
return nil, err
}
output, err := cmd.NewCommandMgr(cmd.WithContext(ctx), cmd.WithTimeout(5*time.Second)).RunWithOptionalSudoAndStdout("ip", "-details", "-json", "link", "show")
command := cmd.NewCommandMgr(cmd.WithContext(ctx), cmd.WithTimeout(5*time.Second))
bridges, err := command.RunWithOptionalSudoAndStdout("ip", "-o", "link", "show", "type", "bridge")
if err != nil {
return nil, err
}
return dockerIPv4BridgePorts(networks, output)
ports, err := command.RunWithOptionalSudoAndStdout("ip", "-o", "link", "show", "type", "veth")
if err != nil {
return nil, err
}
return dockerIPv4BridgePorts(networks, bridges, ports)
}
func dockerIPv4BridgePorts(networks []network.Summary, linkJSON string) (map[string]bool, error) {
func dockerIPv4BridgePorts(networks []network.Summary, bridgeOutput, portOutput string) (map[string]bool, error) {
bridges := make(map[string]bool)
for _, item := range networks {
if item.Driver != "bridge" || item.EnableIPv6 || item.ConfigOnly {
@@ -199,26 +203,36 @@ func dockerIPv4BridgePorts(networks []network.Summary, linkJSON string) (map[str
bridges[name] = true
}
}
var links []struct {
Name string `json:"ifname"`
Master string `json:"master"`
LinkInfo struct {
Kind string `json:"info_kind"`
} `json:"linkinfo"`
}
if err := json.Unmarshal([]byte(linkJSON), &links); err != nil {
return nil, err
}
confirmedBridges := make(map[string]bool)
for _, link := range links {
if link.LinkInfo.Kind == "bridge" && bridges[link.Name] {
confirmedBridges[link.Name] = true
}
}
ports := make(map[string]bool)
for _, link := range links {
if link.LinkInfo.Kind == "veth" && confirmedBridges[link.Master] {
ports[link.Name] = true
for index, output := range []string{bridgeOutput, portOutput} {
for _, line := range strings.Split(output, "\n") {
header, _, _ := strings.Cut(line, "\\")
fields := strings.Fields(header)
if len(fields) == 0 {
continue
}
if len(fields) < 3 || !strings.HasSuffix(fields[0], ":") || !strings.HasSuffix(fields[1], ":") {
return nil, fmt.Errorf("invalid network link entry: %q", line)
}
if _, err := strconv.Atoi(strings.TrimSuffix(fields[0], ":")); err != nil {
return nil, fmt.Errorf("invalid network link entry: %q", line)
}
name, _, _ := strings.Cut(strings.TrimSuffix(fields[1], ":"), "@")
if index == 0 {
if bridges[name] {
confirmedBridges[name] = true
}
continue
}
for i := 3; i+1 < len(fields); i++ {
if fields[i] == "master" {
if confirmedBridges[fields[i+1]] {
ports[name] = true
}
break
}
}
}
}
return ports, nil
@@ -15,7 +15,19 @@
<div v-for="issue in issues" :key="issue.family" class="family-row">
<div class="family-row__body">
<el-tag size="small" type="info" effect="plain">{{ issue.family === 'ipv6' ? 'IPv6' : 'IPv4' }}</el-tag>
<p>{{ issue.message }}</p>
<p>
{{ issue.message }}
<el-link
v-if="issue.helpUrl"
class="ml-1 text-xs"
type="primary"
target="_blank"
rel="noopener noreferrer"
:href="issue.helpUrl"
>
{{ $t('commons.button.helpDoc') }}
</el-link>
</p>
</div>
<el-button
v-if="issue.action"
@@ -55,6 +67,7 @@ import i18n from '@/lang';
interface FamilyIssue {
family: 'ipv4' | 'ipv6';
message: string;
helpUrl?: string;
action?: 'initialize' | 'repair' | 'bind';
}
const props = defineProps<{
@@ -182,10 +182,12 @@ import TaskLog from '@/components/log/task/index.vue';
import FamilyIssues from '@/views/host/firewall/components/family-issues.vue';
import RuleInitialize from '@/views/host/firewall/components/rule-initialize.vue';
import { newUUID } from '@/utils/id';
import { useGlobalStore } from '@/composables/useGlobalStore';
const props = defineProps({
currentTab: String,
});
const { docsUrl } = useGlobalStore();
const goToFirewallSetting = () => routerToName('FirewallSetting');
const goToScriptLibrary = () => routerToNameWithQuery('Library', { uncached: 'true' });
@@ -272,6 +274,10 @@ const familyActions = computed(() =>
return {
family: issue.family === 'IPv6' ? ('ipv6' as const) : ('ipv4' as const),
message: familyIssueText(issue),
helpUrl:
issue.available && issue.reason === 'ipv6_ra_required'
? `${docsUrl.value}/user_manual/hosts/firewall`
: undefined,
action,
};
}),