memo.log

技術情報の雑なメモ

ip route や /proc/net/route のFlagsについて

/proc/net/route を見ると Flags というカラムがある。

% cat /proc/net/route
Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT                                                       
eth0    00000000        01001FAC        0003    0       0       0       00000000        0       0       0
eth1    00000000        01001FAC        0003    0       0       10001   00000000        0       0       0
eth2    00000000        01001FAC        0003    0       0       10002   00000000        0       0       0
eth0    FEA9FEA9        00000000        0005    0       0       0       FFFFFFFF        0       0       0 
eth0    00001FAC        00000000        0001    0       0       0       00F0FFFF        0       0       0
eth1    00001FAC        00000000        0001    0       0       0       00F0FFFF        0       0       0
eth2    00001FAC        00000000        0001    0       0       0       00F0FFFF        0       0       0

これはなんだろうと思って、 ip route コマンドの結果をみてみると、 scope link (隣接(直接宛先と通信できる)のネットワーク)が 0001 と整合しているように見える。

% ip route
default via 172.31.0.1 dev eth0 
default via 172.31.0.1 dev eth1 metric 10001 
default via 172.31.0.1 dev eth2 metric 10002 
169.254.169.254 dev eth0 
172.31.0.0/20 dev eth0 proto kernel scope link src 172.31.7.56 
172.31.0.0/20 dev eth1 proto kernel scope link src 172.31.11.244 
172.31.0.0/20 dev eth2 proto kernel scope link src 172.31.11.75 

route コマンドで見ると Flags が U という文字列で表現されている。

% route                         
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         ip-172-31-0-1.a 0.0.0.0         UG    0      0        0 eth0
default         ip-172-31-0-1.a 0.0.0.0         UG    10001  0        0 eth1
default         ip-172-31-0-1.a 0.0.0.0         UG    10002  0        0 eth2
instance-data.a 0.0.0.0         255.255.255.255 UH    0      0        0 eth0
172.31.0.0      0.0.0.0         255.255.240.0   U     0      0        0 eth0
172.31.0.0      0.0.0.0         255.255.240.0   U     0      0        0 eth1
172.31.0.0      0.0.0.0         255.255.240.0   U     0      0        0 eth2

以下のファイルを見ると、 RTF_UP 0x0001 / route usable /ということで、上記認識で合っているみたい。

/usr/include/linux/route.h

#define RTF_UP          0x0001          /* route usable                 */
#define RTF_GATEWAY     0x0002          /* destination is a gateway     */
#define RTF_HOST        0x0004          /* host entry (net otherwise)   */
#define RTF_REINSTATE   0x0008          /* reinstate route after tmout  */
#define RTF_DYNAMIC     0x0010          /* created dyn. (by redirect)   */
#define RTF_MODIFIED    0x0020          /* modified dyn. (by redirect)  */
#define RTF_MTU         0x0040          /* specific MTU for this route  */
#define RTF_MSS         RTF_MTU         /* Compatibility :-(            */
#define RTF_WINDOW      0x0080          /* per route window clamping    */
#define RTF_IRTT        0x0100          /* Initial round trip time      */
#define RTF_REJECT      0x0200          /* Reject route                 */

参考

android - What's the meaning of /proc/net/rotue columns? especially Flags column - Stack Overflow https://stackoverflow.com/questions/33231034/whats-the-meaning-of-proc-net-rotue-columns-especially-flags-column

【Ruby】ビッグエンディアン・ネットワークバイトオーダー、サンプルメモ

pack("n") で、ネットワークバイトオーダー(ビッグエンディアン)のunsigned short (16bit 符号なし整数)に変換できる。 ビッグエンディアンなので、バイトの上位から並べていく。 例えば、 0xAABBCCDD だったら、0xDDが低い(1桁目側)側なので逆から、AA, BB, CC, DD というふうに格納するのがビッグエンディアンpack("n") の場合は、16bit = 2Byte に変換する。1Byte=16進数で2桁なので2Byteは \xNN\xNN みたいな出力になる。

例)

> [1].pack("n")
=> "\x00\x01"
> [1].pack("n")[0]
=> "\x00"
> [1].pack("n")[1]
=> "\x01"

もとの数値は1、もし2Byteで表現するなら 00000000 00000001 = \x00\x01 で、 pack すると "\x00\x01" になる。見た目は低い方が1だが、Ruby文字列的には

[1].pack("n")[0] => "\x00"

のように下位が0、上位が1なので、人間の見た目にすると、 00000001 00000000 と解釈できる。 つまり、1をバイト上位から並べている=ビッグエンディアン

参考

pack テンプレート文字列 (Ruby 3.0.0 リファレンスマニュアル) https://docs.ruby-lang.org/ja/latest/doc/pack_template.html

【Ruby】複数桁の16進数を10進数に変換する

2FF とかを 767 に変換する。 16進数は文字列リテラルで表現されているものとする。

普通に "2FF" とかだったら hex したらいいけど、

irb(main):089:0> "0x2FF".hex
=> 767

一文字ずつの16進数がまとまった文字列の場合を想定する。 例えば2FF だったら "\xFF\x2" みたいな感じ。

def test(str)
  str.bytes.each.with_index.inject(0) do |sum, (v, index)|
    sum += v << (8 * index)
  end
end

test("\xFF\x2")

参考

each_with_indexしつつinjectしたい - Qiita https://qiita.com/ymstshinichiro/items/366b738ceefd224b5d41

『オブジェクト指向設計実践ガイド』3章メモ

テーマ

  • 依存関係の少ない(疎結合な、変更に強い)コードを書く
  • クラスは自身よりも変更の可能性の低いクラスのみ依存するべき

抑えておきたいこと

具象クラスは、抽象クラスよりも変わる可能性が高い

.

この概念については、「依存オブジェクトの注入」で一度取り上げました。そこでGearが依存していたのは、WheelとWheel.new、そしてWheel.new(rim, tire)でした。極端に具象的なコードに依存していたと言えるでしょう。しかし、コードを変更したあと、つまり、WheelがGearに注入されるようになったあとではどうでしょうか。Gearはとたんに、何かもっと抽象的なものに依存するようになりました。dimeterメッセージに応答できるオブジェクトにアクセスするようになったという事実がそれです。 Rubyに親しんでいると、このような遷移は、当然のように思えるかもしれません。しかし、少し立ち止まって考えてみましょう。同じ対策を、静的型付言語で実現するとすれば、何が必要になったでしょうか。静的型付言語はコンパイラを持ち、そのコンパイラは型に対するユニットテストのような役割を果たします。そのため、単に適当なオブジェクトをGearに注入するわけにはいきません。代わりに「インターフェース」を宣言する必要があるでしょう。diameterをインターフェースの一部として定義し、インターフェースをWheelクラスにインクルードします。その後、注入しようとしているクラスが、そのインターフェースの「一種」だとGearに教えるのです。

.

Wheel をGearへ注入することで、Gearがdeameterに応答するダックタイプに依存するように変える時、実は、さりげなくインターフェースを定義しているのです。このインターフェースは、あるカテゴリーのものはdiameterを持つ、という概念が抽象化されたものです。抽象が、具体クラスから収穫されました。

.

本質的に、抽象はより安定しています。Rubyではインターフェースを定義するために明示的な抽象を宣言する必要はありません。しかし、設計の目的のためなら、仮想的なインターフェースがクラス同様に現実に存在するものであると考えて構いません。

メモ

一応最近自分で書いたコードだと、以下部分はダックタイピング的な感じで書いたと思う(ほぼ無意識だったが。。) header は dump というメソッド(データを表示するふるまい)を持つオブジェクトなら任意であり、このクラスの外から注入される。

      @headers.each do |header|
        header.dump
      end

simple_capture/packet.rb at d1001b2dea56e2807e0257609273d5a650faba82 · kuredev/simple_capture · GitHub

コードによるメモ

以下のコードの問題点は、

  • Gear の Wheel に対する依存が多い
    • クラスの名前、メソッドの名前、オブジェクトの作り方、メソッドの呼び方
  • Wheelの変更によって、Gearの変更が強制される可能性が高い
class Gear
  attr_reader :chainring, :cog, :rim, :tire

  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * Wheel.new(rim, tire).diameter
  end
end

class Wheel
  attr_reader :rim, :tire
  def initialize(rim, tire)
    @rim = rim
    @tire = tire
  end

  def diameter
    rim + (tire * 2)
  end
end

puts Gear.new(52, 11, 26, 1.5).gear_inches
  • 依存オブジェクトの注入を行う
  • 以下のように、Wheel インスタンスの作成をGearの外に移動する
  • これにより、Gearはdiameter を実装するオブジェクトであればどれとでも共同作業ができるようになる
    • Gearはdiameter メソッドへの依存1つを残すのみとなって、GearはWheelに対する知識を減らすことができた
class Gear
  attr_reader :chainring, :cog, :wheel

  def initialize(chainring, cog, wheel)
    @chainring = chainring
    @cog = cog
    @wheel = wheel
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    # ダックタイピング
    # diamter メソッドを喋れるオブジェクトなら何でもOK
    #  これを依存オブジェクトの注入
    #  依存は削減され、diamter メソッドへの依存1つを残すのみとなった
    ratio * wheel.diameter
  end
end

class Wheel
  attr_reader :rim, :tire
  def initialize(rim, tire)
    @rim = rim
    @tire = tire
  end

  def diameter
    rim + (tire * 2)
  end
end

# Gear は diameter を知るDuckを要求する
puts Gear.new(52, 11, Wheel.new(26, 1.5)).gear_inches

外部へのメッセージを隔離する

class Gear
  attr_reader :chainring, :cog, :wheel

  def initialize(chainring, cog, wheel)
    @chainring = chainring
    @cog = cog
    @wheel = wheel
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * diameter
  end

  # 外部へのメッセージ(外部のクラスのデータやメソッドを参照する)は出来るだけ独立させる
  #  変更が発生しやすい箇所のため
  # `外部メソッドならどれでもこのように前もって隔離する対処をできるというわけではありませんが`
  # `それでも自分のコードを調査する価値はあるでしょう。最も脆い依存を探しだし、包み隠しましょう`
  def diameter
    wheel.diameter
  end
end

class Wheel
  attr_reader :rim, :tire
  def initialize(rim, tire)
    @rim = rim
    @tire = tire
  end

  def diameter
    rim + (tire * 2)
  end
end

# Gear は diameter を知るDuckを要求する
puts Gear.new(52, 11, Wheel.new(26, 1.5)).gear_inches

参考リンク

Rubyにおける依存性の注入 - masaki's note https://scrapbox.io/masakis-note/Ruby%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5 →分かりやすい。 要は依存しているオブジェクトをクラスの中で生成するのではなく、期待しているふるまいを持つものを外から入れるようにすればいいと考えれば良さそう。