构建适用于openwrt的switch-lan-play插件

作者:

openwrt是一个非常实用的系统,附带的插件可以配合满足不同的需求。

switch-lan-play是一个远程联机的工具,使用时需要一台设备转发switch的网络请求。一般来说使用的时候打开电脑就足够了,如果经常使用或者手边没有电脑,在路由器上运行明显要好很多。

switch-lan-play官方仓库只提供了win、linux和mac的构建,openwrt的需要自己构建。

写在前面

本文是介绍如何构建,需要有相关技术知识储备,如果只是想直接使用,也需要有基本的相关知识储备。也可以参考下:https://www.huangyunkun.com/2021/04/14/switch-lan-play-openwrt-issue/

构建ipk

openwrt的ipk包除了程序本体以外一般还需要配置、启动脚本和描述信息。

先看构建本身,来源还是git,指向switch-lan-play仓库

https://github.com/spacemeowx2/switch-lan-play.git

主要依赖是libpcap,版本号就跟随官方仓库,当前为0.2.3

include $(TOPDIR)/rules.mk

PKG_NAME:=switch-lan-play
PKG_VERSION:=0.2.3
PKG_RELEASE:=1

PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_SOURCE_URL:=https://github.com/spacemeowx2/switch-lan-play.git
PKG_MIRROR_HASH:=c0c663e3fdc95d6d6e8ab401caa2bfb5b5872e00
#PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)

PKG_LICENSE:=GPLv3
PKG_LICENSE_FILES:=LICENSE.TXT

include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk

CMAKE_BINARY_SUBDIR := build-openwrt

define Package/$(PKG_NAME)
	SECTION:=net
	CATEGORY:=Network
	SUBMENU:=Lan-play
	DEPENDS:=+libpcap +libpthread +libstdcpp +libatomic @!USE_UCLIBC
	TITLE:=Switch Lan Play Client
endef

配置文件主要考虑几个

  • 是否启用
  • 远程服务器IP
  • 远程服务器端口
  • pmtu配置
config switch-lan-play
	option enable 			'0'
	option ifname 			'br-lan'
	option relay_server_host	'127.0.0.1'
	option relay_server_port	'11451'
	option pmtu			'0'

安装的处理主要就是指定各文件位置,这个也是定义在Makefile的

define Package/$(PKG_NAME)/install
	$(INSTALL_DIR) $(1)/usr/bin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/build-openwrt/src/lan-play $(1)/usr/bin/
	$(INSTALL_DIR) $(1)/etc/config
	$(INSTALL_CONF) ./files/switchlanplay.config $(1)/etc/config/switchlanplay
	$(INSTALL_DIR) $(1)/etc/init.d
	$(INSTALL_BIN) ./files/switchlanplay.init $(1)/etc/init.d/switchlanplay
endef

启动脚本和正常启动一致,只是多了一个配置的读取,配置中只有pmtu是可选的。

run_switch_lan_play()
{
    config_get_bool enable $1 enable
    if [ "$enable" -gt 0 ]; then
	local ifname
	config_get ifname $1 ifname
	local relay_server_host
	local relay_server_port
	config_get relay_server_host $1 relay_server_host
	config_get relay_server_port $1 relay_server_port
	config_get pmtu $1 pmtu
	if [ "$pmtu" -gt 0 ]; then
		PROG="lan-play --relay-server-addr $relay_server_host:$relay_server_port --netif $ifname --pmtu $pmtu"
	else
		PROG="lan-play --relay-server-addr $relay_server_host:$relay_server_port --netif $ifname"
	fi
	echo "switch-lan-play client started."
    else
	PROG="lan-play --version"
	echo "switch-lan-play is disabled. check configuration?"
    fi
}

自动化构建

构建的时候是需要openwrt sdk的,同时考虑到不同硬件情况,如果只是构建一个设备类型的ipk还好,如果要提供给不同设备,构建起来比较麻烦。

要自动化构建的话主要有几个点:

  • 不同的sdk包
  • make config的配置

直接在docker中构建,考虑到需要不同的SDK包,这里直接用docker的构建参数来解决

FROM ubuntu:16.04

RUN apt-get update && apt-get install bzip2 file python gcc g++ libncurses5-dev gawk zip xz-utils wget libgetopt-complete-perl make cmake git -y -qq

ARG sdkURL=https://downloads.openwrt.org/releases/19.07.3/targets/ramips/mt7620/openwrt-sdk-19.07.3-ramips-mt7620_gcc-7.5.0_musl.Linux-x86_64.tar.xz
ENV sdkURL ${sdkURL}
ARG targetFolder=target
ENV targetFolder ${targetFolder}

RUN echo ${sdkURL}
RUN wget -q ${sdkURL} -O /sdk.tar.xz && mkdir /sdk && tar -xf /sdk.tar.xz -C /sdk && cp -R /sdk/openwrt-* /sdk/openwrt-sdk

重新配置feeds.conf


WORKDIR /sdk/openwrt-sdk
RUN cp feeds.conf.default feeds.conf && echo "src-git openwrt_switch_lan_play https://github.com/htynkn/openwrt-switch-lan-play.git" >> feeds.conf

RUN ./scripts/feeds update -a && ./scripts/feeds install -a

config的配置挺花了点精力,因为一般构建都是make menuconfig进入图形化界面选择lan-play的项,但是自动化肯定不能这么弄。

只有先用defconfig生成配置,然后合并我们需要的配置项。找了下linux中有一个merge_config脚本可以实现。

RUN wget https://raw.githubusercontent.com/torvalds/linux/v4.7/scripts/kconfig/merge_config.sh && chmod +x merge_config.sh
RUN make defconfig
RUN echo "CONFIG_PACKAGE_luci-app-switch-lan-play=y" >> .config-fragment
RUN echo "CONFIG_PACKAGE_switch-lan-play=y" >> .config-fragment

RUN ./merge_config.sh -m .config .config-fragment

准备好了直接构建

RUN make package/luci-app-switch-lan-play/compile && make package/switch-lan-play/compile

自定义源服务

构建出的ipk可以直接安装,但是提供在线安装源是最简单的。除了ipk文件本身,另外一个必要的文件就是Packages文件,其中说明了相关包的内容和信息。内容如下:

Package: simple-obfs
Version: 0.0.5-4
Depends: libc, libpthread
License: GPLv3
Section: net
Architecture: aarch64_armv8-a
Installed-Size: 85471
Filename: simple-obfs_0.0.5-4_aarch64_armv8-a.ipk
Size: 86228
SHA256sum: 1de2460ae99c5123b444e961e1f0fd5c6ea378612978fcf408180a5a51530488
Description:  Simple-obfs is a simple obfusacting tool, designed as plugin server of shadowsocks.

要生成这个信息用脚本处理即可。为了方便后期使用,这里就保持一个较好的文件结构。

RUN make package/luci-app-switch-lan-play/compile && make package/switch-lan-play/compile
RUN rm -rf ${targetFolder} && mkdir -p ${targetFolder} && find . | grep lan-play | grep ipk | xargs -I {} cp {} ${targetFolder}
RUN gcc scripts/mkhash.c -o mkhash && mv mkhash /usr/local/bin && chmod +x /usr/local/bin/mkhash
RUN cd ${targetFolder} && /sdk/openwrt-sdk/scripts/ipkg-make-index.sh . >> Packages
RUN find ${targetFolder} | xargs zip -ur target.zip

这样生成的文件就包含了所有必要信息,放在一般的http服务器上即可。

如果要放在Github Pages也行,但Github Pages没有目录浏览功能,看不到有哪些文件。这里可以自己生成,一个python脚本即可。

""" Build index from directory listing

make_index.py </path/to/directory> [--header <header text>]
"""
from __future__ import print_function
import os.path, time

INDEX_TEMPLATE = r"""

<html>
<head>
<title>${header}</title>
<meta name="description" content="${header}"/>

</head>
<body>
    <h2>Index of ${header}</h2>
    <p>
    <table>
        <tbody>
            <tr>
                <th valign="top"></th>
                <th><a href="?C=N;O=D">Name</a></th>
                <th><a href="?C=M;O=A">Last modified</a></th>
                <th><a href="?C=S;O=A">Size</a></th>
                <th><a href="?C=D;O=A">Description</a></th>
            </tr>
            <tr>
                <th colspan="5"><hr></th>
            </tr>
            <tr>
                <td valign="top"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC "
                    alt="[PARENTDIR]"></td>
                <td><a href="../">Parent Directory</a></td>
                <td>&nbsp;</td>
                <td align="right">-</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <th colspan="5"><hr></th>
            </tr>

            % for name in dirnames:
            <tr>
                <td valign="top"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII= "
                    alt="[DIR]"></td>
                <td><a href="${name}">${name}</a></td>
                <td align="right">${time}</td>
                <td align="right">-</td>
                <td>&nbsp;</td>
            </tr>
            % endfor
            % for name in filenames:
            <tr>
                <td valign="top"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC "
                    alt="[DIR]"></td>
                <td><a href="${name}">${name}</a></td>
                <td align="right">${time}</td>
                <td align="right">-</td>
                <td>&nbsp;</td>
            </tr>
            % endfor
            </p>
        </tbody>
    </table>
</body>
</html>
"""

EXCLUDED = ['index.html']

import os
import argparse

# May need to do "pip install mako"
from mako.template import Template

def fun(dir,rootdir):
    print('Processing: '+dir)
    filenames = [fname for fname in sorted(os.listdir(dir))
              if fname not in EXCLUDED and os.path.isfile(dir+fname)]
    dirnames = [fname for fname in sorted(os.listdir(dir))
            if fname not in EXCLUDED  ]
    dirnames = [fname for fname in dirnames if fname not in filenames]
#    header = os.path.basename(dir)
    f = open(dir+'/index.html','w')
    print(Template(INDEX_TEMPLATE).render(dirnames=dirnames,filenames=filenames, header=dir,ROOTDIR=rootdir,time=time.ctime(os.path.getctime(dir))),file=f)
    f.close()
    for subdir in dirnames:
        try:
            fun(dir+subdir+"/",rootdir+'../')
        except:
            pass

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("directory")
    parser.add_argument("--header")
    args = parser.parse_args()
    fun(args.directory+'/','../')

if __name__ == '__main__':
    main()

生成效果:https://htynkn.github.io/openwrt-switch-lan-play/openwrt/

评论

29 条对“构建适用于openwrt的switch-lan-play插件”的回复

  1. 新手

    x86/64有2个错误 导致运行不了 改动下可以用了 新版openwrt 添加/usr/share/rpcd/acl.d/luci-app-switch-lan-play.json
    {
    “luci-app-switch-lan-play”: {
    “description”: “Grant UCI access for luci-app-switch-lan-play”,
    “read”: {
    “uci”: [ “switchlanplay” ]
    },
    “write”: {
    “uci”: [ “switchlanplay” ]
    }
    }
    }
    这个只是看不到画面 另外一个 研究了下 编译的lan-play 只支持libpcap.so.0.8
    我只找到libpcap1。。。 直接复制 软连接后 ok了 In -s /usr/lib/libpcap.so.0 /usr/lib/libpcap.so.0.8

    1. Nighting

      兄弟最后命令。。。应该是LN啊 1也打成0…..
      ln -s /usr/lib/libpcap.so.1 /usr/lib/libpcap.so.0.8

    2. 匿名

      谢谢留言,问题1已经包含在新的包中。
      问题2,我再研究下

    3. 匿名

      libpcap应该是openwrt版本问题,已经升级到最新的19.07.7了

  2. 匿名

    博主用的路由器是哪款?

    1. htynkn

      极路由3

  3. 匿名

    Switch LAN Play
    Play local wireless Switch games over the Internet.

    Service status: – 已停止

    成功安装了,但是开不了,显示服务器停止,要怎么打开?

    1. htynkn

      启动以后看下有上角,是不是要手动保存

      1. 511568

        也碰到了同样的情况,同样是已停止,在别处也找了x86的版本一样不行,路由器固件是重新刷的lean,右上角也没有保存了。。

        1. htynkn

          那就只有手动执行下命令,看下具体报错,一般都是libpcap版本问题

          1. 匿名

            请问该如何手动设置命令,可否告知一下,谢谢啦

          2. 匿名-续

            我看了一下软件包的libpcap版本,是1.9.0-1,会有问题吗?

    2. 匿名

      一样的情况

  4. 匿名

    R4S 软路由,应该选择哪个版本的插件即可安装使用,小白一枚,请指教,谢谢!

    1. htynkn

      R4S没用过,不过看资料我感觉不支持。可以编译

      1. 匿名

        如果大佬能在空闲之时可否编译一下,万分感谢!

        1. htynkn

          编译不了,没有相关设备,建议自行学习下,或者用PC

  5. 匿名

    大佬牛逼

  6. 匿名

    Switch LAN Play
    Play local wireless Switch games over the Internet.

    Service status: – 已停止

    成功安装了,但是开不了,显示服务器停止,要怎么打开?
    x86 64 2个ipk都安装了

    1. htynkn

      1.是否为标准openwrt系统?版本是否为19.07.7?
      2.右上角是否提示未提交?需要提交才能生效
      3.填写的参数是否正确?不正确的话有一定几率导致不工作

      1. 匿名

        我的openwrt固件版本是
        OpenWrt R20.5.20 / LuCI Master (git-20.117.60969-420c61a)
        是不是更新了一点就无法使用呢?

        1. htynkn

          这个都不是官方的openwrt,官方版本号目前到19

    2. 匿名

      ln -s /usr/lib/libpcap.so.1 /usr/lib/libpcap.so.0.8 用ssh登录路由器 用这个命令就解决了。感谢楼上的大神

  7. 哈哈哈

    要是有个n1能用的就好了

  8. 匿名

    /usr/lib/lua/luci/dispatcher.lua:1334: module ‘luci.cbi’ not found:
    no field package.preload[‘luci.cbi’]
    no file ‘./luci/cbi.lua’
    no file ‘/usr/share/lua/luci/cbi.lua’
    no file ‘/usr/share/lua/luci/cbi/init.lua’
    no file ‘/usr/lib/lua/luci/cbi.lua’
    no file ‘/usr/lib/lua/luci/cbi/init.lua’
    no file ‘./luci/cbi.so’
    no file ‘/usr/lib/lua/luci/cbi.so’
    no file ‘/usr/lib/lua/loadall.so’
    no file ‘./luci.so’
    no file ‘/usr/lib/lua/luci.so’
    no file ‘/usr/lib/lua/loadall.so’
    stack traceback:
    [C]: in function ‘require’
    /usr/lib/lua/luci/dispatcher.lua:1334: in function ‘_cbi’
    /usr/lib/lua/luci/dispatcher.lua:1023: in function ‘dispatch’
    /usr/lib/lua/luci/dispatcher.lua:478: in function

    安装后进入错误,是那里没设置?

  9. 大湿

    大佬,能帮忙编译一下switch-lan-play的华硕梅林(386_41634_koolshare)这个的插件吗?小弟不会交叉编译,谢谢!邮箱地址是[email protected]

    1. htynkn

      没用过梅林

  10. 17Dstsl

    大佬,能否编译一份sdk:openwrt-sdk-22.03.0-rc5-armvirt-64_gcc-11.2.0_musl.Linux-x86_64.tar.xz适用的插件,我自行编译一直报错,万分感谢!!

  11. 进驻

    虚拟机 openwrt-19.07.7-x86-64-combined-ext4.img
    直通了网卡
    lan-play 配置好
    DBI联网访问文件很卡,游戏联机会掉线

    同样的服务器,在 Mac 系统下运行官方 lan-play 二进制可执行文件一切正常

    是虚拟机的锅?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注