Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): wildcard lan interface name support #729

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

woshikedayaa
Copy link
Contributor

Background

#724

Checklist

Full Changelogs

实现了 #724 的请求,现在支持了对于 lan_interfaces的匹配支持。

匹配规则支持 + 匹配一个字符,* 匹配多个字符。

代码变化

主要变动的是匹配部分,首先入口变成了 bindLanWildcard

global.LanInterface = common.Deduplicate(global.LanInterface)
for _, ifname := range global.LanInterface {
	if err = core.bindLanWildcard(ifname, global.AutoConfigKernelParameter); err != nil {
		return nil, fmt.Errorf("bindLan: %v: %w", ifname, err)
	}
}
  

内部实现是,首先根据匹配的 pattern 来匹配。如果不是 pattern 就只绑定单独的if。

func (c *controlPlaneCore) bindLanWildcard(ifname string, autoConfigKernelParameter bool) error {
	if !netutils.IsInterfaceNameIsWildcard(ifname) {
		return c.bindLan(ifname, autoConfigKernelParameter, false)
	}

	// bind to current existed matched-interfaces
	matcher, err := netutils.NewInterfaceMather(ifname)
	if err != nil {
		return err
	}
	interfaces, err := net.Interfaces()
        // ......
	for _, iface := range interfaces {
        // ......
	}
	// listen new link creation
	if e := c.addNewLinkBindLanCb(ifname, autoConfigKernelParameter, true); e != nil {
		return fmt.Errorf("%w: %v", err, e)
	}
	return nil
}

addNewLinkBindLanCb方法添加了一个 keep bool 参数来决定是否需要持续监听。

另外就是 bindLan 方法的变化。因为全局已经有一个 通过监听匹配表达式的监听回调,所以用一个参数表面如果监听删除的网卡是来自 wildcard 就不添加新的监听了。

func (c *controlPlaneCore) bindLan(ifname string, autoConfigKernelParameter bool, isFromWildcard bool) error {
	if !isFromWildcard && netutils.IsInterfaceNameIsWildcard(ifname) {
		return c.bindLanWildcard(ifname, autoConfigKernelParameter)
	}

	if autoConfigKernelParameter {
		SetSendRedirects(ifname, "0")
		SetForwarding(ifname, "1")
	}
	if err := c._bindLan(ifname); err != nil {

		var notFoundErr netlink.LinkNotFoundError
		if !errors.As(err, &notFoundErr) {
			return err
		}
		if isFromWildcard {
			return err
		}
		// Not found error.
		// Listen for `NEWLINK` to bind.
		c.log.Warnf("Link '%v' is not found. Bind LAN program to it once it is created.", ifname)
		if e := c.addNewLinkBindLanCb(ifname, autoConfigKernelParameter, false); e != nil {
			return fmt.Errorf("%w: %v", err, e)
		}
		return nil
	}

	// Listen for `DELLINK` and add `NEWLINK` callback to re-bind.
	if err := c.addLinkCb(ifname, unix.RTM_DELLINK, func(realIfname string) {
		c.log.Warnf("Link deletion of '%v' is detected. Bind LAN program to it once it is re-created.", realIfname)
		// The interface retrieved through wildcard will be handled by the global wildcard callback.
		// no need to monitor it here
		if isFromWildcard {
			return
		}
		if e := c.addNewLinkBindLanCb(realIfname, autoConfigKernelParameter, false); e != nil {
			c.log.Errorf("Failed to add callback for re-bind LAN program to '%v': %v", ifname, e)
		}
	}, false); err != nil {
		return fmt.Errorf("failed to add re-bind callback: %w", err)
	}
	return nil
}

最后,文档将在合并后添加。谢谢各位开发者的耐心查看代码。

另外还有个小问题,个人未修复(不知道咋修)。具体表现为,在一个被泛型匹配到的网卡被删除后,会报错
[Jan 04 16:49:01] ERROR addQdisc: Link not found

往提出修改意见修复(小bug 不影响使用)。

Issue Reference

#724

Test Result

在我的电脑工作正常。

帖一个测试脚本

#!/bin/bash
# Define interface name
IFACE="br-test"

# Check if running as root
if [ "$EUID" -ne 0 ]; then
    echo "Please run as root (use sudo)"
    exit 1
fi

# Create a dummy interface
ip link add ${IFACE} type dummy
if [ $? -eq 0 ]; then
    echo "Created virtual interface: ${IFACE}"
    # Bring the interface up
    ip link set ${IFACE} up
    echo "Interface ${IFACE} is up"
else
    echo "Failed to create interface"
    exit 1
fi

# Wait for user input
echo "Interface is ready. Type 'ok' to delete it:"
while true; do
    read input
    if [ "$input" = "ok" ]; then
        break
    else
        echo "Type 'ok' to delete the interface:"
    fi
done

# Delete the interface
ip link delete ${IFACE}
echo "Interface ${IFACE} has been deleted"

@jschwinger233
Copy link
Member

golang 使用 tab 作为缩进,请用 go fmt 格式化代码。

@woshikedayaa
Copy link
Contributor Author

woshikedayaa commented Jan 7, 2025

已经重新 commit , cmd/internal 文件大面积爆红是因为上次提交的时候没有 format

顺便修了个 typo


另外,在群内讨论到后面可能需要单独修改 Callback的逻辑,以便于在 route 阶段匹配 lan 口名称。此PR 需要建议来修改可能。
@LostAttractor

@mzz2017
Copy link
Contributor

mzz2017 commented Feb 17, 2025

抱歉,回复有些晚。

或许我们可以用较为通用的 shell pattern,go 的标准库 path.Match 可以做到这一点:

package path

// Match reports whether name matches the shell pattern.
// The pattern syntax is:
//
//	pattern:
//		{ term }
//	term:
//		'*'         matches any sequence of non-/ characters
//		'?'         matches any single non-/ character
//		'[' [ '^' ] { character-range } ']'
//		            character class (must be non-empty)
//		c           matches character c (c != '*', '?', '\\', '[')
//		'\\' c      matches character c
//
//	character-range:
//		c           matches character c (c != '\\', '-', ']')
//		'\\' c      matches character c
//		lo '-' hi   matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
// The only possible returned error is [ErrBadPattern], when pattern
// is malformed.
func Match(pattern, name string) (matched bool, err error)

其中 * 匹配多个,? 匹配一个。

为了降低复杂度,我们或许可以对所有表达式通用这个 matcher,而不需要 InterfaceMather 接口并给出两个实现。

@woshikedayaa 你怎么看?

@woshikedayaa
Copy link
Contributor Author

woshikedayaa commented Feb 19, 2025

今天才看到邮件消息,这个回复可能稍晚。

我觉得不错,支持更多了,且更标准,比我自己搓的 re来匹配好得多,我后面会提交一个新的commit来修改。

@mzz2017
Copy link
Contributor

mzz2017 commented Feb 19, 2025

@woshikedayaa 👍🏻👍🏻👍🏻

@mzz2017
Copy link
Contributor

mzz2017 commented Feb 19, 2025

目前的代码逻辑是:

  1. 对于每个接口,如果因为不存在而绑定失败,启动一个协程监听 netlink NEW 消息,link NEW 时调用 bindLan,并退出协程
  2. 对于每个成功 bindlan 的接口,启动一个协程监听 netlink DEL 消息,在 DEL 的时候(和 1 一样)启动一个协程监听 netlink NEW 消息,link NEW 时调用 bindLan,并退出协程

由于改为了 wild match,不能每次都退出协程,可以考虑:

  1. 在 newControlPlaneCore 时,将所有 patterns 加入一个 slice,启动一个协程,监听 netlink NEW/DEL 消息:对所有 link NEW & FlagUp == 0(初次创建)的接口,匹配某个 pattern 则进行 bindLan;对所有 DEL 消息,尝试将接口名从 bindedLanSet 中删除(bindedLanSet 见下,检查和操作过程上锁)
  2. 对所有 patterns,对当前所有 ifs 进行遍历,匹配某个 pattern 则进行 bindLan
  3. bindLan 函数全程上锁,并检查 bindedLanSet,如果已经 bind 了则退出,在 bindLan 成功返回前将接口名加入 bindedLanSet 里去
  4. 将协程的退出加入到 deferFuncs 的清理中去,可以用 context.WithCancel() 控制生命周期。

@mzz2017
Copy link
Contributor

mzz2017 commented Feb 20, 2025

#758 已做实现,可以一同测试一下

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants