memo.log

技術情報の雑なメモ

Amazon Linux2 で glibc をコンパイルする

ハマったところメモ

最新のglibcコンパイルしようとすると、以下のようなエラーが各種ライブラリについて出てきた。1つ1つ潰していくのは大変すぎるのと、標準リポジトリでは公開されていないバージョンのライブラリも必要になってくるようだったのでインストールする glibc のバージョンを下げることにした。

These critical programs are missing or too old: as

現時点で 2 year old の glibc-2.27Amazon Linux release 2 (Karoo) x86 にインストールする。

インストール場所

という制約があるようなので、それぞれ別にする。 今回は↓

手順

(再現試験していないので抜け漏れあるかも)

# 必要なライブラリをインストール
% sudo yum install glibc-static
# ソースをダウンロード
% cd src
% git clone git://sourceware.org/git/glibc.git -b glibc-2.27
# ビルド~インストール
% cd ../build
% ../src/glibc/configure --prefix=/usr/local/my-glibc
% make
% make install

参考リンク

USP友の会:CentOS6.xのgccでスタティックリンクする方法 https://www.usptomo.com/PAGE=20120228GCC

The GNU C Library https://www.gnu.org/software/libc/sources.html

RubyでEtherIPパケットを作るメモ書き(RAWソケットで生データを書き込む)

require "socket"

# EtherIPヘッダも含んでそれらも含めた上側のデータ
def trans_data
  # IP Header: 20Byte
  # MAC Header: 14Byte
  # EtherIP Header: 2Byte
  # とりあえず、EtherIPヘッダ+自然に生成されたEtherヘッダ+IPヘッダをベタで。
  bynary_data =
    0x30000ab1a91e677a0a2b383bbb0a080045000014d2b84000ff61e09aac1f0bf408080808.to_s(2).rjust(36 * 8, "0")

  data_byte_arr = bynary_data.scan(/.{1,8}/)
  data_byte_arr.map! { |byte| byte.to_i(2).chr } # TO ASCII
  data_byte_arr.join
end

# 以下により、Etherヘッダ+IPヘッダ(TypeはEtherIP)が入る
sock = Socket.new(
  Socket::AF_INET,
  Socket::SOCK_RAW, # TCP とか UDP とかではなく、IPヘッダ以上は自分で書くことができる?
  97 # EtherIP
)

sock.bind(Socket.sockaddr_in(nil, "172.31.11.244"))
sock.send(trans_data, 0, Socket.sockaddr_in(nil, "8.8.8.8"))

できあがるデータ

f:id:kuredev:20210111010429p:plain

リリースしたばかりのGemをインストールしようとしたら「Could not find gem ...」

Gemをリリース(アップデート)して、アップデートしたVersionを実際にインストールしてみようとVersion指定して bundle install しようとしたら以下のエラーが。

Could not find gem xxxx in any of the gem sources listed in your Gemfile.

ところが、5分くらいしたらインストールできるようになったので、 https://rubygems.org/ 側でキャッシュが効いていたみたい。

【Ruby 3.0】RBSの生成方法を色々試してみるメモ

以下の3つの方法を順番に試してみる。 引数/戻り値の型は記載されていてほしいのでtypeprof が動くのならそれを利用するのがベターそう。 各方法の解説は参考リンクのページが詳しい。

  • rbs prototype rb
    • Rubyファイルを指定して静的に解析
    • 引数/戻り値の型は基本 untyped になる
  • rbs prototype runtime
    • Ruby実行ファイルを指定して解析
    • 引数/戻り値の型は untyped になる
  • TypeProf
    • Ruby実行ファイルを指定して解析
    • 引数/戻り値の型は実行結果から自動で推定される

環境:

 % ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [aarch64-linux]

rbs prototype rb

Rubyファイル

  • とりあえずIntegerのパラメータを1つだけ取るコンストラクタを持つ Kure クラスを準備してみる。
  • run はその値を返す
  • test1 を固定で返す
class Kure
  # @param num [Integer]
  def initialize(num)
    raise "Parameter Invalid" unless num.is_a?(Integer)

    @num = num
  end

  def run
    @num
  end

  def test
    1
  end
end

RBS作成

  • コンストラクタと run メソッド引数と戻り値の型は無い。
  • test メソッドは 1 を固定で返すことが読み込まれて1が記載されている。
 % rbs prototype rb test.rb
class Kure
  # @param num [Integer]
  def initialize: (untyped num) -> untyped

  def run: () -> untyped

  def test: () -> 1
end

rbs prototype runtime

Rubyファイル(実行含)

class Kure
  # @param num [Integer]
  def initialize(num)
    raise "Parameter Invalid" unless num.is_a?(Integer)

    @num = num
  end

  def run
    @num
  end

  def test
    1
  end
end

kure = Kure.new(1)
puts kure.run
puts kure.test

RBS作成

  • ほとんど prototype rb と同様だが test メソッドは実装的には 1 を必ず返すが今回はその中身は解析されず、実行結果から内容を出力しているので untyped となっている
 % rbs prototype runtime -R test.rb Kure
1
1
class Kure
  public

  def run: () -> untyped

  def test: () -> untyped

  private

  def initialize: (untyped num) -> untyped
end

TypeProf

Rubyファイル(実行含)

rbs prototype runtimeと同じ

RBS作成

  • 引数と戻り値の型が推定されていて、出力にも記載されている
 % typeprof test.rb 
# Classes
class Kure
  @num: Integer

  def initialize: (Integer num) -> Integer
  def run: -> Integer
  def test: -> Integer
end

ちなみに以下のように返る戻り値の型が変動するメソッドの場合は、

  def test2
    @num == 1 ? 1 : "not 1"
  end

以下の内容が出力された。

def test2: -> (Integer | String)

参考

pocke.hatenablog.com

qiita.com

【Ruby】シンプルなネットワークブリッジを作ってみた

シンプルなネットワークブリッジをRubyで作ってみた。

github.com

動作確認した環境

使い方

以下のように記述して、sudo で実行

require_relative "lib/simple_bridge"

br = SimpleBridge::Bridge.new("eth0", "eth1")
br.run

できること

  • 2つのネットワークインターフェース間でパケットを転送する

できないこと

参考にした実装

Ruby Raw Socket on Linux (ruby 1.9.3, linux x86_64) · GitHub

bind_socket.rb · GitHub

感想

Rubyで低レベルのプログラミングはなかなかサンプルも少ないし、難しい…。 PacketFuのようなパケットキャプチャツールの実装も覗いてみたが、ネットワークデバイスを操作する部分はCで実装していた。 多分、その方が色々細かい部分は実装しやすいのだと思う。 とはいえ、Rubyの書きやすさは捨てがたい&Rubyで低レイヤプログラミングしてみたらどんなもんだろうという試みでもあったので、とりあえず簡易実装までは完遂できてよかった。

関連投稿

kure.hatenablog.jp

kure.hatenablog.jp

kure.hatenablog.jp

【Ruby】ioctl で NWインターフェースのフラグ(ifr_flags)情報を取ってみるメモ

概要

C for Linux 2』という本に ioctl でネットワークインターフェースの情報を取得するサンプルプログラムがあった。 以下のような実行イメージ。

 % ./ifinfo eth0
name=eth0
UP BROADCAST MULTICAST 
mtu=9001
addr=172.31.7.56
dstaddr=172.31.7.56
broadaddr=172.31.15.255
netmask=255.255.240.0

Rubyでネットワークプログラムを書いているのだが、あまりサンプルプログラムや情報が少ないので、まずは真似て勉強してみようということで、上記プログラムの一部と同じ動作結果を得られるプログラムを書いてみたメモ。 全部やりきるのは労力がかかるので、一部(フラグ: UP BROADCAST MULTICAST )の出し方だけ調べてみた。あとは、ioctl に渡す引数の仕様等を調べれば同じやり方で実装できるはず。 C言語の実装は上記本のサンプルプログラムに記載があるので、それと突き合わせながら確認すると良いと思う。

メモ

ソースコード

gist.github.com

実行結果

100001000011

これだと何だかわからないが、 UP BROADCAST MULTICAST となっているインターフェースの情報となっている。 linux/if.h に ifreq.ifr_flags に格納される各フラグのビットの位置が記載されている。 ネットですぐに拾えたものだと以下等(なぜか spotify さん)。 つまり、左から1番目(1bit目)がONになっているのは IFF_UP 0x1 がONになっているためなのでUPだと判断できる。 2bit目以降も突き合わせていけば整合が確認できる。

linux/if.h at master · spotify/linux · GitHub

コードのメモ

3行目の以下は

SIOCGIFFLAGS = 0x8913

ioctl の引数に渡す定数。ioctl はデバイスに対して様々な情報を取得したり設定できるシステムコール。 渡す引数によって実行を制御できるが、 SIOCGIFFLAGS の場合は、インターフェースのフラグを取得できる、ということらしい。 ちなみに手元のPC(Amazon Linux2(arm64))では /usr/include/bits/ioctls.h のパスに当該ファイルは存在した。 ネットだと↓に同じようなものが落ちていた。

ioctls.h

6行目では、 ioctl に渡す引数の ifreq 構造体を準備する。 C言語では ifreq 構造体のポインタを ioctl に渡すと取得された情報をその構造体に格納してくれる。 Rubyの場合はC言語的なポインタは準備できないので、パックした文字列を渡すと、その文字列を破壊的に変更してくれるらしい(ドキュメント等で確認できておらず、挙動から推定) 今回の場合は、ASCII文字列に変更すると都合がいいようなのでそのように変換しておく。 このへんの動作仕様は

https://gist.github.com/k-sone/8036832

の実装を参考にさせてもらった。

ifreq = ["eth0"].pack("a4")

10行目では結果を取得している。 ifreq 構造体 Man page of NETDEVICE では最初に char ifr_name[IFNAMSIZ]; /* Interface name */ が入っているのでこれを除外して、どうやらその次のデータから取得したデータが入ってくるらしい。このあたりの動きは完全に結果から類推しているので、きちんと仕様として理解したいのだが、パッとドキュメント等には見当たらず、多分実装の深い部分を追っていかないと分からなそうだったので、これ以上深追いはしていない。

で、同じ定数はRubyでも Socket::IFNAMSIZ で参照できる(手元の環境では 16 だった)。 フラグが入っている ifr_flags は上記 ifreq の仕様をみると short 型で2Byteなのでその分だけ取ってくる。

result = ifreq[Socket::IFNAMSIZ, 0x02].dup

最後に結果を16進数(文字列)を数値(10進数)に変えて、2進数(文字列)に変えて、フラグのビットを確認する。

puts result[1].ord.to_s(2) + result[0].ord.to_s(2)

感想

いまだに分からないところだらけだが、Rubyで ioctl を触る雰囲気だけつかめてよかった。