Farseerfc的小窩 - usb//farseerfc.me/2019-02-07T02:14:00+09:00用 usbip 轉發 raspberry pi 的 USB 鍵盤鼠標給 Arch Linux 的 PC2019-02-07T02:14:00+09:002019-02-07T02:14:00+09:00farseerfctag:farseerfc.me,2019-02-07:/usbip-forward-raspberrypi.html<p>惠狐 <a class="reference external" href="/links.html#megumifox">megumifox</a> 寫了篇 <a class="reference external" href="https://blog.megumifox.com/public/2019/02/06/%E7%94%A8pulseaudio%E5%B0%86%E7%94%B5%E8%84%91%E7%9A%84%E5%A3%B0%E9%9F%B3%E7%94%A8%E6%89%8B%E6%9C%BA%E6%94%BE%E5%87%BA%E6%9D%A5/">用PulseAudio將電腦的聲音用手機放出來</a>
,文末提到想知道我怎麼用樹莓派轉發 USB 的,於是寫篇文章記錄一下。</p>
<div class="section" id="id1">
<h2>起因</h2>
<p>家裏有個裝了 Arch Linux ARM 的樹莓派3B 閒置着,裝了 Arch Linux ARM 偶爾上電更新一下,
不過因爲性能實在不適合做別的事情於是一直在吃灰。某日 <del>給老婆安利幻想萬華鏡</del><ins>和老婆看片</ins>
的時候, <del>老婆不吃安利於是遷怒鍵盤鼠標</del><ins>鍵盤鼠標被長長的 USB 線扯着感覺很難受</ins>
,於是偶發奇想,能不能利用一下樹莓派的多達 4 個 USB 2.0 端口接鼠標鍵盤呢,
這樣鼠標鍵盤就可以跟着樹莓派來回走,不用拖着長長的 USB 線了。</p>
<p>上網搜了一下, Linux 環境有個 usbip 工具正好能做到這個。原理也很直觀, usbip 能把 USB …</p></div><p>惠狐 <a class="reference external" href="/links.html#megumifox">megumifox</a> 寫了篇 <a class="reference external" href="https://blog.megumifox.com/public/2019/02/06/%E7%94%A8pulseaudio%E5%B0%86%E7%94%B5%E8%84%91%E7%9A%84%E5%A3%B0%E9%9F%B3%E7%94%A8%E6%89%8B%E6%9C%BA%E6%94%BE%E5%87%BA%E6%9D%A5/">用PulseAudio將電腦的聲音用手機放出來</a>
,文末提到想知道我怎麼用樹莓派轉發 USB 的,於是寫篇文章記錄一下。</p>
<div class="section" id="id1">
<h2>起因</h2>
<p>家裏有個裝了 Arch Linux ARM 的樹莓派3B 閒置着,裝了 Arch Linux ARM 偶爾上電更新一下,
不過因爲性能實在不適合做別的事情於是一直在吃灰。某日 <del>給老婆安利幻想萬華鏡</del><ins>和老婆看片</ins>
的時候, <del>老婆不吃安利於是遷怒鍵盤鼠標</del><ins>鍵盤鼠標被長長的 USB 線扯着感覺很難受</ins>
,於是偶發奇想,能不能利用一下樹莓派的多達 4 個 USB 2.0 端口接鼠標鍵盤呢,
這樣鼠標鍵盤就可以跟着樹莓派來回走,不用拖着長長的 USB 線了。</p>
<p>上網搜了一下, Linux 環境有個 usbip 工具正好能做到這個。原理也很直觀, usbip 能把 USB
端口上的數據封裝成 IP 協議通過網絡轉發出去,從而兩個網絡間相互聯通的電腦就可以遠程轉發 USB 了。
設置好的話,就像是一臺 PC 多了幾個位於樹莓派上的 USB 端口,插上樹莓派的 USB 設備統統作爲 PC
的設備。</p>
<p>這篇文章假設有一個裝了 Arch Linux 的 PC ,和一個裝了 Arch Linux ARM 的樹莓派,
並且兩者間能通過網絡互相訪問到。別的發行版上大概也可以這麼做,只是我沒有試過。 usbip
工具似乎普遍被發行版打包了,除此之外需要的也只是 Linux 內核提供好的功能而已。</p>
</div>
<div class="section" id="arch-linux-arm">
<h2>設置 Arch Linux ARM 的樹莓派端</h2>
<p>假設樹莓派上面網絡已經設置妥當,開機插電就能自動聯網。接下來安裝 usbip 工具:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> sudo pacman -Syu usbip</span>
</pre></div>
<p>然後需要記錄一下樹莓派的 IP 地址:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> ip addr</span>
<span class="code-line"><span class="go">3: wlan0: ......</span></span>
<span class="code-line"><span class="go">inet 192.168.0.117/24 brd 192.168.0.255 scope global noprefixroute wlan0</span></span>
<span class="code-line"><span class="go">......</span></span>
</pre></div>
<p>接下來給 udev 添加一個規則,當插入 usb 設備的時候,執行我的腳本 usbipall.sh
把 usb 設備通過 usbip 共享出去:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> cat /etc/udev/rules.d/usbipall.rules</span>
<span class="code-line"><span class="go">ACTION=="add", SUBSYSTEM=="usb", RUN+="/usr/bin/bash /usr/local/bin/usbipall.sh"</span></span>
</pre></div>
<p>這個 rules 文件 <a class="reference external" href="https://github.com/farseerfc/dotfiles/blob/master/usbiprpi/usbipall.rules">可以在我的 dotfiles 裏面找到</a> 。</p>
<p>然後規則調用的 usbipall.sh 我這麼寫的, <a class="reference external" href="https://github.com/farseerfc/dotfiles/blob/master/usbiprpi/usbipall.sh">文件同樣在我的 dotfiles 裏面</a> :</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="ch">#!/bin/sh</span></span>
<span class="code-line"><span class="o">(</span></span>
<span class="code-line"><span class="nv">allusb</span><span class="o">=</span><span class="k">$(</span>usbip list -p -l<span class="k">)</span></span>
<span class="code-line"><span class="k">for</span> usb in <span class="nv">$allusb</span></span>
<span class="code-line"><span class="k">do</span></span>
<span class="code-line"> <span class="nv">busid</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$usb</span><span class="s2">"</span> <span class="p">|</span> sed <span class="s2">"s|#.*||g;s|busid=||g"</span><span class="k">)</span></span>
<span class="code-line"> <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"1-1.1"</span> <span class="o">]</span></span>
<span class="code-line"> <span class="k">then</span></span>
<span class="code-line"> <span class="c1"># ignoring usb ethernet</span></span>
<span class="code-line"> <span class="k">continue</span></span>
<span class="code-line"> <span class="k">fi</span></span>
<span class="code-line"> <span class="nb">echo</span> <span class="s2">"</span><span class="k">$(</span>date -Iseconds<span class="k">)</span><span class="s2">: Exporting </span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"> usbip <span class="nb">bind</span> --busid<span class="o">=</span><span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"><span class="k">done</span></span>
<span class="code-line"><span class="o">)</span> >>/var/log/usbipall.log <span class="m">2</span>><span class="p">&</span><span class="m">1</span></span>
</pre></div>
<p>這個腳本做了這樣幾件事。</p>
<ol class="arabic simple">
<li>調用 <code class="code">
usbip list --local</code>
列出本地所有 usb 設備。</li>
<li>針對每個設備<ol class="arabic">
<li>取出它的 busid</li>
<li>判斷是不是樹莓派的 USB 以太網卡,不是的話繼續</li>
<li>通過 <code class="code">
usbip bind --busid=</code>
命令把這個 usb 設備導出到網上</li>
</ol>
</li>
<li>最後把所有輸出記錄到 /var/log/usbipall.log 日誌裏面</li>
</ol>
<p>樹莓派這邊設置就完成了。從此之後插入的 usb 設備就會統統導出出去。</p>
<p>這裏需要注意一下,啓用了 udev 規則之後,就沒法插鍵盤鼠標到樹莓派上控制它了……我都是從另一端 ssh
上樹莓派操作的。如果有什麼地方設置錯誤,可能需要把樹莓派的 SD 卡拔下來插到電腦上,刪除掉 rules
文件……</p>
<p>仔細檢查設置正確了之後,重新載入 udev 規則,或者重啓樹莓派:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">#</span> systemctl restart systemd-udevd</span>
</pre></div>
<p>這樣樹莓派這邊就設置好了。</p>
</div>
<div class="section" id="arch-linux-pc">
<h2>設置 Arch Linux 的 PC 端</h2>
<p>同樣假設 PC 這邊也已經聯網。接下來同樣安裝 usbip 工具:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> sudo pacman -Syu usbip</span>
</pre></div>
<p>然後我寫了個小腳本去鏈接樹莓派端, <a class="reference external" href="https://github.com/farseerfc/dotfiles/blob/master/usbiprpi/usbiprpi3.sh">這個文件 usbiprpi3.sh 也在我的 dotfiles</a>:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="ch">#!/bin/sh</span></span>
<span class="code-line"><span class="nv">rpi3</span><span class="o">=</span><span class="s2">"192.168.0.117"</span></span>
<span class="code-line"></span>
<span class="code-line">modprobe vhci-hcd</span>
<span class="code-line"></span>
<span class="code-line"><span class="nv">allusb</span><span class="o">=</span><span class="k">$(</span>usbip list -p -r <span class="nv">$rpi3</span> <span class="p">|</span> cut -d<span class="s2">":"</span> -f1 -s <span class="p">|</span> sed <span class="s1">'s|^[ \t]*||;/^$/d'</span><span class="k">)</span></span>
<span class="code-line"><span class="k">for</span> busid in <span class="nv">$allusb</span></span>
<span class="code-line"><span class="k">do</span></span>
<span class="code-line"> <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"1-1.1"</span> <span class="o">]</span></span>
<span class="code-line"> <span class="k">then</span></span>
<span class="code-line"> <span class="c1"># ignoring usb ethernet</span></span>
<span class="code-line"> <span class="k">continue</span></span>
<span class="code-line"> <span class="k">fi</span></span>
<span class="code-line"> <span class="nb">echo</span> <span class="s2">"Attaching </span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"> usbip attach --remote<span class="o">=</span><span class="nv">$rpi3</span> --busid<span class="o">=</span><span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"><span class="k">done</span></span>
</pre></div>
<p>其中腳本第一行填入上面記錄下來的樹莓派的 IP 地址,接下來腳本做了這麼幾件事:</p>
<ol class="arabic simple">
<li>用 modprobe 確認加載 vhci-hcd 通用虛擬鍵鼠驅動</li>
<li>用 <code class="code">
usbip list --remote=</code>
列出遠程設備上已經導出了的 USB 設備,取出他們的 busid</li>
<li>對每個設備用 <code class="code">
usbip attach</code>
接上該設備</li>
</ol>
<p>然後就已經準備妥當,接下來是見證奇蹟的時刻:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> sleep <span class="m">10</span><span class="p">;</span> sudo ./usbiprpi3.sh</span>
<span class="code-line"><span class="go">Attaching 1-1.4.3</span></span>
<span class="code-line"><span class="go">Attaching 1-1.4.1</span></span>
</pre></div>
<p>因爲只有一套鍵盤鼠標,所以先 sleep 個 10 秒,在此期間快速把鍵鼠拔下來插到樹莓派的 USB 口上去。
如果對自己手速沒自信也可以把時間設長一點。然後用 root 權限執行 usbiprpi3.sh 。</p>
<p>一切正常的話,先能觀測插上樹莓派的鍵盤鼠標被樹莓派初始化了一下,比如鍵盤燈會亮,
然後這些設備會被導出出去,從而鍵盤燈滅掉,然後 10 秒等待結束後他們被遠程接到了 PC 端,
又會被初始化一下,同時 PC 端這邊會有上述 Attaching 的輸出。然後鍵盤鼠標就能像平常一樣用啦。</p>
</div>
<div class="section" id="id3">
<h2>使用體驗</h2>
<p>因爲就是通過 IP 轉發 USB 嘛,所以就和普通地接 USB 的體驗差不多,當然前提是網絡環境足夠穩定。
在我家間隔 5 米到無線路由器的環境下,基本感覺不到網絡延遲的影響。
通過這種方式聊天上網應該和直接接 USB 設備完全一樣。本文就是在通過樹莓派轉發的前提下用鍵盤打字寫的。</p>
<p>不過如果網絡負載本身就很大的話,可能會一些延遲,比如我開着 OBS 直播打東方的時候,原本就手殘
的我感覺更加手殘了……</p>
<p>試過拿着樹莓派在房間到處走,走到無線信號覆蓋不到的地方, usbip 會斷掉,PC 上的現象就像是 USB
設備被拔下來了……所以如果無線網絡不穩的話,可能需要對上面腳本做個循環?不過那樣可能會用起來很彆扭吧。</p>
<p>以及,上述操作 usbip 是走 TCP 3240 端口,數據包大概完全沒有加密,所以考慮安全性的話,
最好還是在內網環境使用。不過轉念一想,萬一有別人接上了我導出出去的 USB ,也就是截獲我的鍵盤,
PC 這邊沒法 attach 設備了,應該馬上會發現吧。我敲打 sudo 之類命令的時候 shell 裏面沒有回顯,
就不會再繼續敲密碼了。而且似乎對攻擊者也沒有什麼好處?要是他 usb attach 到了我的設備上,
我就能控制他的鍵盤了耶~</p>
</div>