mirror of
https://github.com/fatedier/frp.git
synced 2026-05-15 08:05:49 -06:00
[GH-ISSUE #961] 反馈一个关于关于MTU值引起的frp穿透问题 #764
Labels
No labels
In Progress
WIP
WaitingForInfo
bug
doc
duplicate
easy
enhancement
future
help wanted
invalid
lifecycle/stale
need-issue-template
need-usage-help
no plan
proposal
pull-request
question
todo
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: github-starred/frp#764
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @huxuansong on GitHub (Nov 1, 2018).
Original GitHub issue: https://github.com/fatedier/frp/issues/961
Originally assigned to: @blizard863 on GitHub.
Issue is only used for submiting bug report and documents typo. If there are same issues or answers can be found in documents, we will close it directly.
(为了节约时间,提高处理问题的效率,不按照格式填写的 issue 将会直接关闭。)
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
What version of frp are you using (./frpc -v or ./frps -v)?
v0.21.0
What operating system and processor architecture are you using (
go env)?Frps: Lede X86_64版
Frpc: ubuntu 14.04
Configures you used:
[web-bs]
type = http
local_ip = 127.0.0.1
local_port = 80
use_compression = true
custom_domains = www.mydomain.com
[ssh-bs]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 10022
Steps to reproduce the issue:
1.两个光猫的MTU值不同,其中一个是1500,另外一个1492
2.Ubuntu系统默认的MTU值为1500
3.内网连接SSH正常,frp内网穿透连接SSH异常
Describe the results you received:
其中网络的MTU值1492的那台,出现一个奇怪的故障,
内网访问完全正常;外网出错,访问部分html页面会出错(内网完全正常);
并且内网SSH连接正常,而经过Frp外网穿透的SSH连接断开,原因不明
Describe the results you expected:
Additional information you deem important (e.g. issue happens only occasionally):
Can you point out what caused this issue (optional)
不知道这个算不算BUG,
猜测与Ubuntu的默认的MTU值1500>外网的MTU值1492有关;
尝试修改Ubuntu的MTU值为1492,故障解决
相关的错误和Log记录在我的Blog上有记录
https://www.cainiao.io/archives/820
@bufeii commented on GitHub (Dec 24, 2019):
遇到同样的情况。
@blizard863 commented on GitHub (Apr 3, 2020):
可以 tcpdump 分别在 frpc 和 frps 的机器上抓下包导出 pcap 文件格式,方便的话可以发出来我分析下。我这边测试没有复现这样的问题。
@KusakabeShi commented on GitHub (Oct 25, 2021):
我遇到一样的问题,情境是这样:
当双方mtu都是1500,但是中间某个路由器MTU小于1500
防火墙又过滤ICMP封包导致Path MTU Discovery失败,就会造成此情况
以下是我的推测
正常情况,TCP连线建立时,内核会读取本地出口网卡的MTU,在SYN包里设置MSS(Maximum Segment Size)
服务器收到SYN,也会读取入口网卡的MTU,在返回的SYN+ACK里面设置MSS
双方交换完对方的MSS以后,就用小的值作为传输的MSS
如果今天双方网卡MTU都很大,但是中间某一跳路由器MTU很小怎么办?
此时就会用到Path MTU Discovery。
这个中间路由器就会丢弃该封包,并返回
ICMP Fragmentation Needed (Type 3, Code 4)收到以后,内核就会减少这个tcp session的MSS,并重新传输该封包
但是现今,很多公有云商家防火墙默认禁止ICMP传输。还有很多地方,例如公司网路,为了安全性也不允许ICMP传输
此时Path MTU Discovery就会失败,内核不知道要缩小MSS,导致丢包,进而导致整个tcp session中断
我的具体表现为通过frp,ssh连线成功以后,执行这行
1350逐渐增大,当他大于中间设备的MTU之时,连线中断
或是
cat一个大档案,htop之类,都会导致连线中断大部分的情况,可以在其中一边把网卡MTU手动调小来解决问题。
TCP握手的时候,linux kernel取网卡MTU,直接用了小的MSS
双方会交换MSS之后以,小的为准,这个session就会用这个MTU
但是部分时候不允许这么做,例如网路上的docker容器服务,没有
CAP_NET_ADMIN。还有一些情境,比如办公设备,没有root权限,不能更动设备MTU
在这些场景下,frp的tcp模式變得完全无法使用,十分不便
Feature Request
增加一个设定档选项
Custom MSS当启用时,tcp模式下,linux底下使用setsockopt API,在connect()或listen()以前,先使用
不要读取网卡MTU,而是手动设定MSS。之后tcp连线建立时,SYN就会带有我们指定的MSS了。
再之后内核就会按照tcp协议,用指定的MSS自动帮我们分包了
Custom MSS還可以允許3種情況:None: 默認值,不使用setsockopt。MSS全權交給kernel決定Int: 使用setsockopt手工設定MSSAuto: 建立一條測試用tcp,一端是echo端,一端發送測試資料None模式建立真正tcpInt模式建立真正的tcp其他平台要怎么弄可能还要调查一下。 windows好像也是类似,这边提到
如果平台不支援这个选项(正常来说只要一边有支援就好,标准tcp交换MSS以后要用小的为准),就只能用备援方案了
服务端/客户端会先交换MSS,之后这个session在每次send的时候,单次最多只发送设定好byte数,剩下的留在buffer内,来解决这个问题。
虽然这样做性能可能会比较不好,需要自己维护一个buffer
每次都send()->flush()->send()->flush()来保证单个封包不超过指定大小
@blizard863 commented on GitHub (Oct 26, 2021):
@KusakabeSi frp 版本是多少 ?
@KusakabeShi commented on GitHub (Oct 26, 2021):
frps 0.37.1
frpc 0.37.1
@fatedier commented on GitHub (Oct 26, 2021):
@KusakabeSi 感谢你上述的说明,很有帮助。
按照你上面描述的路径,服务端和客户端的 MTU 都是 1500,中间路由器是 1492,且禁止 ICMP 包,那么不仅仅是 frp,其他所有应用都会受到影响。那么,是不是不应该仅仅在 frp 层面去解决这一类问题,而是当做一个通用的问题去解决?
在 Go 中,由于跨平台的特性,要设置这些参数通常比较复杂,我们尽量不考虑这一类需要针对不同平台/系统去分别设置的场景。
当然,还有一个折中的方案,可以使用 KCP 协议作为 frp 的通信协议,底层是 UDP。KCP 层面的 MTU 目前设置的是 1350,这个倒是可以开放出来允许配置。你可以测试一下使用 KCP 的场景下,你上述的问题是否还存在?
@KusakabeShi commented on GitHub (Oct 26, 2021):
測試過了,kcp正常使用
但是kcp有比較耗cpu,或是QoS,或因不公平競爭,被某些服務商禁止使用,等等的其他問題
沒錯,其他基於tcp,而且通過這個mtu 1492節點的應用都會受到影響。
因為我家是PPPoE撥號,mtu 1492
但是我的本地網卡的mtu也是1492,所以本地應用一直都沒問題
我這裡mtu 1500的網卡是docker裡面的網卡,frps運行在docker內部
但是大部分後端都放行ICMP,Path MTU Discovery正常運作,所以我也沒發現這個問題
直到我最近把frpc佈署在一個擋掉icmp的容器裡面,才發現這個問題
以我個人的use case來說,可以把本地docker裡面的網卡mtu設置成1492解決
但是我想到某些場景,例如公司網路(為了安全性擋掉icmp+防止p2p擋掉udp)+公司電腦(本地無root),就有用的上這個配置的必要了
雖然我不知道會不會真的有公司用限制這麼多網路配置就是了(笑)
真要實現,可能是平台獨佔的功能了,畢竟setsockopt沒有比較高階的API,只能針對linux/windows/freebsd分別去做適配
linux應該長這樣(更新,這個寫法不行。linux連線分兩步驟,先創建sock,再connect。setsockopt要安插在中間才可以。golang的Dial把創建sock和connect綁在一起了)
@KusakabeShi commented on GitHub (Oct 26, 2021):
試著弄了個linux客戶端實現,要自己實現一個net.Dial,不能用官方給的
因為linux連線分兩步驟,先創建socket,再connect。
setsockopt要安插在中間才可以。
然而golang的Dial把socket()和connect()綁在一起了
所以要自己實現一個Dial,分別呼叫socket->setsockopt->connect
然後再轉成原本的net.Conn物件
當啟用CustomMSS的時候,用自己的dialWithMSS,而不是golang的net.Dial
參考一下main 第二行
一樣拿到net.Conn,之後操作一樣
可以看到MSS變成1400了
感覺再frps比較容易實現,可以試著在listen以後,accept以前加入
setsockopt看看是否生效@KusakabeShi commented on GitHub (Oct 26, 2021):
frps比較容易實現,listen以後,accept以前加入setsockopt,經測試有效。不用自己實現listen和accept了
tcpdump可以看到mss=1400
@KusakabeShi commented on GitHub (Oct 26, 2021):
通用層解決,要嘛修改MTU,要嘛用geneva之類的工具攔截/抓包/修改封包的MSS欄位。但是也有不能使用的時候(分別需要CAP_NET_ADMIN和CAP_NET_RAW權限)
我個人覺得仅仅在 frp 层面去解决这一类问题是有意義的。解決以後就可以在沒有上述權限的情況下,在frp的基礎上,承載原本無法通過的服務了
freebsd的實現應該和linux非常類似
windows的應該也能做到,我在winsock的文檔中有發現還linux的setsockopt功能參數一模一樣的API
除了fd變成Hendle以外。
印象中linux和windows的TCP/IP stack都是抄自BSD,只是要一一適配確實挺麻煩的
Custom MSS變成linux版獨有功能我倒是感覺還好,大部分情境下frps都架設在linux底下,而且只需要一邊是linux就可以了真的不行就用WSL吧
不知道能不能做成插件的形式,此插件僅限linux使用之類
@KusakabeShi commented on GitHub (Oct 31, 2021):
So... 有点好奇,作者大大打算怎么解决?
setsockoptCustomMSS选项,或是fallback到方案3conn.SetNoDelay(true)就好@fatedier commented on GitHub (Nov 1, 2021):
2 和 3 都不是很理想的解决方案,并且上述你描述的场景其实是一个通用的问题,会影响到所有的网络应用,而不是由于某一个项目自身的架构所造成的问题。
在没有找到优雅的解决方案前,我倾向于暂时不解决,目前看上去实际上遇到这样场景的用户非常少,并不值得引入一个不太优雅的配置增加复杂度。
@alexallen1 commented on GitHub (Mar 26, 2024):
遇到一样的问题。腾讯云搭建frps,海外路由web界面跑frpc。在海外访问内网穿透端口进入路由web界面快,大陆进入路由web界面慢,而且是非常慢,呈现出完全不一样的速度。大陆连腾讯云15ms瞬发跑满没有速度瓶颈。速度不一致猜测是mtu问题。
@github-actions[bot] commented on GitHub (Apr 17, 2024):
Issues go stale after 21d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.