ふ~ん

メモ書き

rubyを使って擬似digコマンドをつくってみる

●まえがき
 暇つぶしにつくりました
 最小限の実装(パラメータ直打)となっています
 需要はないと思いますが載せておきます


●実装及びテスト環境
 Linux Mint 17.2 Rafaela 64-bit
 ruby 2.2.1p85 (rvmで入れました)
 net-dns ver.0.8.0 (モジュールです gem install net-dnsで入れました)


●実際に使った様子
 このブログのURLを名前解決した結果です
 キャッシュサーバにキャッシュがあるため、すぐ応答がきています
f:id:wiasth:20150823042617p:plain
見方がわからない人は、DNS クライアントを作ってみよう (2)を見てみるといいかも


ソースコード

require 'socket'
require 'rubygems'
require 'net/dns'
require 'benchmark'

srcip = "127.0.0.1" #自身のIPアドレス
sport = rand(1024..65535) #自身のポート番号(乱数)
dport = 53 #宛先ポート番号(DNS => 53)
dstip = "127.0.1.1" #DNSのIPアドレス
seturl = "wiasth.hatenablog.com" #名前解決したいURL

@udps = UDPSocket.open
@udps.bind(srcip, sport)

que = Net::DNS::Packet.new(seturl)
@udps.send(que.data, 0, dstip, dport)

time = Benchmark.realtime do
    @r = IO::select([@udps], nil, nil,5)
end

if @r
    res = @udps.recvfrom(1024)
    res = Net::DNS::Packet::parse(res)
    time = time*1000
    time = time.round(10)
    puts "\n#{res}\n"
    puts ";; Query time: #{time} msec\n\n"
else
    time = time*1000
    time = time.round(12)
    puts "\n;; connection timed out: no servers could be reached\n"
    puts ";; Query time: #{time} msec\n\n"
end
@udps.close

 わずか35行

 やっていることは
  1.パラメータのセット
  2.Aレコードを作りDNSサーバへ送る
  3.最大5秒間応答待ち、5秒たっても応答がなければ終了
  4.応答があればそのパケットを表示する
 の4つです
 
  1.パラメータのセット

srcip = "127.0.0.1" #自身のIPアドレス
sport = rand(1024..65535) #自身のポート番号(乱数)
dport = 53 #宛先ポート番号(DNS => 53)
dstip = "127.0.1.1" #DNSのIPアドレス
seturl = "wiasth.hatenablog.com" #名前解決したいURL

   プログラムに直接パラメータを打ち込んでいます
   最低限動作させるだけならこれで十分だと思います


  2.Aレコードを作りDNSサーバに送る

@udps = UDPSocket.open
@udps.bind(srcip, sport)

que = Net::DNS::Packet.new(seturl)
@udps.send(que.data, 0, dstip, dport)

   上の2行で自身のIPとUDPのポート番号をbindしています
   下の2行では、セットしたURLを元にAレコードをつくりDNSサーバへ送信しています

  ※補足
   que = Net::DNS::Packet.new(seturl)とは?
    net-dnsというモジュールをつかうことで1行でレコードをつくっています
    Packet.newのデフォルトはAレコードとなっているみたいです
    他のレコードの作成の仕方として、例えば192.168.1.1を逆引きしたい(PTRレコード)ときは、
    que = Net::DNS::Packet.new("192.168.1.1", Net::DNS::PTR)
    でいけると思います(他のレコードも同じで、パラメータとNet::DNS::レコードの種類でいけるっぽい?)


  3.最大5秒間応答待ち、5秒たっても応答がなければ終了

time = Benchmark.realtime do
    @r = IO::select([@udps], nil, nil,5)
end
・
・
・
else
    time = time*1000
    time = time.round(12)
    puts "\n;; connection timed out: no servers could be reached\n"
    puts ";; Query time: #{time} msec\n\n"
end

   上にあるtimeではbenchmarkという時間をはかるモジュールを使っています
   囲っている部分の時間をはかれるようです
   今回はパケットを送ってから応答が検出されるまでをはかっています(これで大丈夫なのかは分からない)
   ⇒ 追記;応答を受信したときの日時 - パケットを送信したときの日時 で時間を出した方が良かった

   タイムアウトまでの時間はnslookupコマンドのデフォルトと同じ5秒としました
   パケットを送ってから5秒の間@r(応答)が検出されなければif文のelseの方に入り、タイムアウトメッセージと送信から応答までの時間を表示して終わりとなっています


  4.応答があればそのパケットを表示する

if @r
    res = @udps.recvfrom(1024)
    res = Net::DNS::Packet::parse(res)
    time = time*1000
    time = time.round(10)
    puts "\n#{res}\n"
    puts ";; Query time: #{time} msec\n\n"

   @r(応答)が検出されたらif文のifの方に入ります
   ここでは、レスポンスのデータをNet::DNS::Packet::parseに格納することでレスポンス解析しています
   net-dnsの仕様で「puts レスポンス」するだけで詳細表示されるようになっているみたいです
   レスポンスの中身を表示し、送信から応答までの時間を表示して終わりとなっています


応答時間テスト
 digコマンドと比較してみました
 計測方法:
  1.dnsmasq(127.0.1.1)のキャッシュを一度クリア
  2.dnsmasqに対して11回「wiasth.hatenablog.com」の名前解決要求
  3.はじめの1回目をキャッシュ用とみなし破棄、2-11回目の応答時間結果を10で割る
  1〜3の操作を両者で行ないました(こんなやり方で良いのかは疑問ですが)
  digコマンドに合わせてmsecのコンマ以下は切り捨てとしました

 結果:
  digコマンド ⇒ 25.9msec
  つくったやつ ⇒ 25.0msec
  (環境次第な感じ?)


●最後に(補足等)
 結果表示自体はdigコマンドのほうが今回つくったやつより早いです
 理由としては、rubyでは結果表示(puts文等)がかなりのロスになるからです
 応答時間のはかり方が正しいのかあやしい(プログラム的な意味で)ので、上記のテストはあまり参考にしないでください

 間違えてる気がするので、テストは見なかったことにしてください

 今回使用したnet-dnsモジュールを使うとrubyで簡単なDNSサーバを実装できます
 net-dnsの他にDNS関連のモジュールでrubyDNSというものもありますが、個人的には今回使ったnet-dnsが使いやすく感じました

 DNSで既存処理以外のことをさせたいというときにBINDに手を加えるよりはnet-dnsを使って実装したほうが楽な気がします
 しかしながら、net-dnsは情報が少ないみたい(ぐぐってもあんまりでてこない)なので、機会があれば載せてみようと思います

 以上

ローカルネットワーク内のみで使えるメールサーバをたてよう

●はじめに
・外部には公開せず、ローカルネットワーク内のみの検証用としてメールサーバをたてたい
・とりあえずメールサーバをたててみたいが、どうすればよいか分からない
このような人向けです

※設定ファイルに余計な設定をしてしまっているところがあったり、説明が間違えているところがあるかもしれません

 また、2014/12月時点での導入方法なので、バージョンがあがるなどの環境の変化によりこの方法ではできなくなってしまうかもしれません、ご了承ください

 

●今回紹介するメールサーバ導入の概要
 最終的には以下のような構成になります(仮想環境です)

    f:id:wiasth:20141227004849p:plain

・前提
 1.VMwarePlayerにメールサーバ兼クライアントとしてCentOS6.6を、クライアントとしてLubuntu14.10をたて終わっている

 2.SSLなどのセキュリティ機能は使わない

 3.DNSを使用せず、サーバを指定するときは直接IPアドレスを指定する

 

・目標
 server1(server1@example.local)、user1(user1@example.local)というアカウントをつくり、メールクライアントを用いてローカルネットワーク内でメールのやり取りをする

 

・環境
 1.VMwarePlayer 6.0.2
 2.CentOS 6.6 (メールサーバ兼クライアント用)
 3.Lubuntu 14.10 (クライアント用) ※クライアント用はどんなOSでもよい
 4.Postfix 2.6.6-6 (メール送信サーバ) プロトコルとしてSMTPを使っている
 5.Dovecot 2.0.9-8 (メール受信サーバ) プロトコルとしてIMAPかPOPを使っている ⇒今回はIMAPを使う

 6.Evolution 2.32.3-34 (メールクライアント)  ← マイクロソフト社のOutlookのようなもの

 

●viエディタの簡単な使い方(必要のない方は飛ばしてください)
 今回、設定などを編集するのにviエディタ(ウィンドウズでいうメモ帳のようなもの)を使う

 このviエディタを使う上で重要なことは、Insertキーを押すと挿入モードに、Escキーを押すとコマンドモードになるということ(だと思う)

 例
 ・文字を書きたかったら →Insertキーを押すと書けるように
 ・ファイル上書きしたかったら →Escキーを押す、:w を入力しエンター
 ・編集を終了したかったら →Escキーを押す、:q を入力しエンター
 ・ファイルを上書きせず編集を終了したかったら →Escキーを押す、:q! を入力しエンター

 今回はこの程度の知識があれば十分です

 

●流れ
 1.いきなり構成通り設定するのではなく、はじめにメールサーバ兼クライアント(CentOS)内でメールのやり取りができるようにする
 2.メールサーバ用CentOS及びクライアント用Lubuntuの持つインターフェースにIPアドレスを割り振り、IP通信ができることを確認する
 3.192.1.1.0/24内でもメールを使えるように設定を少しいじる
 4.メールサーバ用CentOSとクライアント用Lubuntu間でのメールのやり取りをテストし、無事動作したら目標達成


●手順1
 概要:メールサーバ兼クライアント(CentOS)内でメールのやり取りをできるようにする

 

 メールサーバ兼クライアント(CentOS6.6)を起動、ターミナルを開く
 以下のコマンドを実行
 
 su  ←rootにする
 yum -y update  ←念のためアップデート

 vi /etc/selinux/config

  SELINUX=enforcing
  ⇒ SELINUX=disabled に変更(selinuxを無効化)
 (※viエディタの使い方は少し上の項目にあります)

 

 次に、使用するポート番号のファイアウォールを無効化する
  a.システム管理ファイアウォールを選ぶ

  f:id:wiasth:20141227005936p:plain


  b.ファイアウォールの設定画面が開くと以下のようなポートリストがでるので、メール(SMTP)にチェック

 f:id:wiasth:20141227010028p:plain


  c.次にその他のポートを選ぶ

  d.右側にある追加ボタンを押し、ポート番号110番のpopポート番号143番のimaptcp,udpをリストに追加
  e.以下の画面のようになったら、最後に適用ボタンを押し反映させる

f:id:wiasth:20141227010058p:plain

 

 上記の作業が終わったら、一旦CentOSを再起動する

 

 ・postfix関連から設定
  su  ←rootにする
  yum -y install postfix  ←postfixのインストール(元から入っているかも)
  service sendmail stop  ←Sendmailを止める(そもそも入ってないかも)
  chkconfig sendmail off  ←Sendmail自動起動を止める(そもそも入ってないかも)

  service postfix start  ←postfixを起動
  chkconfig postfix on  ←postfix自動起動を有効化

 

 ・設定ファイルの編集
  vi /etc/postfix/main.cf

   #mydomain = domain.tld
   ⇒ mydomain = example.local に変更(#をはずしドメイン名変更 自分のドメイン名を書くところ)

   #myorigin = $mydomain
   ⇒ myorigin = $mydomain に変更(#はずす)

   inet_interfaces = localhost
   ⇒ inet_interfaces = all に変更(localhostのみしか受け付けなくなっているため変更)

   inet_protocols = all
   ⇒ inet_protocols = ipv4 に変更(今回はIPv4しか使わないため変更)

   mydestination = $myhostname, localhost.$mydomain, localhost
   ⇒ mydestination = $myhostname, localhost.$mydomain, localhost, example.local に変更(宛先ドメインとして今回使うドメイン名を追加しておく)

   #mynetworks = ・・・の下
   ⇒ mynetworks = localhost を追加(はじめはlocal内で動かすだけなので自分のネットワークはlocalhostのみ)

   #home_mailbox = Maildir/
   ⇒ home_mailbox = Maildir/ に変更(#はずす メールボックスはMaildir形式を使うようにするという意味)

   これで /home/ユーザ名/Maildirに保存されるように?

   #mail_spool_directory = /var/mail
   ⇒ mail_spool_directory = /var/mail/ に変更(#はずし、/追加 各ユーザのメールディレクトリは/var/mail/以下につくるという意味)これやらなくていいかも

 

  設定を保存し編集を終えたら、
  service postfix restart でpostfixを再起動


 ・メールアカウントをつくる(今回はuser1とserver1)

  useradd user1 -s /sbin/nologin  ←user1ユーザの追加

  passwd user1  ←user1のパスワード登録(今回は「12345」としました)

  useradd server1 -s /sbin/nologin  ←server1ユーザの追加

  passwd server1  ←server1のパスワード登録(今回は「12345」としました)

  後でパスワードを使うことになるので覚えておく

  ※今回は説明をしやすくするため、パスワードを「12345」としましたが、これは破られやすいパスワードなので必ず推測されにくいパスワードを使ってください


 ・user1やserver1は/var/spool/mail/以下の書き込み権がないので権限を与える
  chmod 777 /var/spool/mail

 

 以上でメール送信はできるようになったので、telnetというターミナル上でメールなどのやり取りができるものを使ってメール送信テストを行なう

 

 yum -y install telnet  ←telnetのインストール
 telnet localhost 25  ←ポート25番にlocalhostで接続

 

 220 localhost.localdomain ESMTP Postfix
 helo localhost
 250 localhost.localdomain
 mail from: user1@example.local  ←メールの送信元アドレス
 250 2.1.0 Ok
 rcpt to: user1@example.local  ←メールの宛先アドレス
 250 2.1.5 Ok
 data  ←メールの本文かきますよというコマンド
 354 End data with <CR><LF>.<CR><LF>
 helo postfix  ←メールの本文
 .  ←コンマはメールの本文を書き終えましたよというコマンド
 250 2.0.0 Ok: queued as ・・・
 quit  ←telnetを終了するコマンド
 221 2.0.0 Bye

 

 このテストが終わった後
 ls /var/mail/user1 でファイルができていることを確認

 ls /home/user1/Maildir でファイルができているはず(ディレクトリが見つからないとでなければ問題なし)


 ・dovecot関連の設定

  yum -y install dovecot  ←dovecotのインストール
  service dovecot start  ←dovecotを起動
  chkconfig dovecot on  ←dovecot自動起動を有効化

 

 ・設定ファイル(dovecot.conf)の編集
  vi /etc/dovecot/dovecot.conf

   #protocols = imap pop3 lmtp
   ⇒ protocols = imap pop3 lmtp に変更(#はずす 使用プロトコルの列挙)

   #listen = *, ::
   ⇒ listen = * に変更(#はずし、::消す 今回はIPv4しか使わないので::(IPv6)へのバインドをしない)


 ・設定ファイル(10-mail.conf)の編集
  vi /etc/dovecot/conf.d/10-mail.conf

   #mail_location =
   ⇒ mail_location = maildir:~/Maildir に変更(#はずし、ファイル場所をかく)


 ・設定ファイル(10-auth.conf)の編集
  vi /etc/dovecot/conf.d/10-auth.conf

   #disable_plaintext_auth = yes
   ⇒ disable_plaintext_auth = no に変更(#はずし、noにする 平文認証の許可)

   auth_mechanisms = plain
   ⇒ auth_mechanisms = plain login に変更(login追加 いらないかも?)


  設定を保存し編集を終えたら、
  service dovecot restart でdovecotを再起動

 

 以上でメール受信できるようになったので、telnetを使ってターミナル上でメール受信ファイルを見るテストを行なう(IMAPを使う)

 telnet localhost 143  ←ポート143番にlocalhostで接続


 ※1、2などの数字も忘れずに

  Connected to localhost.
  1 login user1 12345  ←「1 login ユーザー名 パスワード」でログインする
  Logged in
  2 list "" *  ←ホームディレクトリの一覧を見るコマンド(""の後半角空白あり)
  * List () "." "INBOX"
  2 OK List completed.
  3 select INBOX  ←INBOXの中身を見るコマンド
  1 EXISTS  ←1通のメールがある
  1 RECENT  ←最新のメールは1通
  3 OK Select completed.
  4 fetch 1 body[]  ←1通目のメールを見るコマンド(送信テストしたものが見れる)
  From: user1@example.local
  To: undisclosed-recipients:;

  helo postfix
  )
  4 OK Fetch completed.
  5 logout
  5 OK logout completed.

 

 テスト完了

 

 ・Evolutionというメールクライアントを使って送受信テストする

  yum -y install evolution  ←evolutionをインストール 

 

  インストール後、アプリケーションオフィスEvolutionを選ぶ

   f:id:wiasth:20141227011906p:plain


  設定画面がでてくるので、2回進むを押す
  以下の画面でuser1の情報を登録、追加情報の下にあるボックスのチェックをはずす

f:id:wiasth:20141227011945p:plain


  この画面では、サーバ指定欄に127.0.0.1を指定しておく

  f:id:wiasth:20141227012005p:plain


  この画面でも、サーバ指定欄に127.0.0.1を指定しておく

  f:id:wiasth:20141227012020p:plain


  適用ボタンを押した後、IMAPを使うためのパスワードをきかれるので、作成したアカウントのパスワードを入れる(今回ならuser1、server1ともに「12345」)

 

  evolutionのタブの編集設定を選ぶ

     f:id:wiasth:20141227012105p:plain


  設定画面がでたら、右側にある追加ボタンを押し、server1についても先ほどと同じ設定を行なう

f:id:wiasth:20141227012135p:plain

 

  設定が終わったら、evolutionのタブの表示現在のビューメッセージ表示にチェック、またスレッドでグループ化にもチェックを入れる

f:id:wiasth:20141227012225p:plain


  次にevolutionのタブの表示配置の3つの項目すべてにチェックを入れる
  すると、左側にサイドバーができるf:id:wiasth:20141227012252p:plain

 

  まず、server1からuser1へテストメールを送ってみる

  左側サイドバーのserver1@example.localを選択し、新規ボタンを押す
  以下の画面のようなメールを書き、送信ボタンを押す

f:id:wiasth:20141227012343p:plain


 左側サイドバーの送信済みを選択し、先ほど書いたメールがあれば成功

 

 次にテストメールをuser1が受信できているか確認する
 左側サイドバーのuser1以下の受信箱を選択し、以下のようにテストメールが届いていれば成功

f:id:wiasth:20141227012431p:plain


 メールサーバ兼クライアント(CentOS)内でメールのやり取りをできるようになった

 

●手順2
 概要:CentOS、Lubuntuの持つインターフェースにIPアドレスを割り振り、IP通信ができることを確認する

 

 ・一旦CentOSをシャットダウンし、VMwarePlayerを開く
  a.CentOSを選択し、右下の仮想マシン設定の編集ボタンを押す
  b.設定画面の下の方にある追加ボタンを押しネットワークアダプタを選択
  c.次へボタンを押し、そのまま完了ボタンを押す

f:id:wiasth:20141227012603p:plain


  d.ネットワークアダプタを選択し、カスタムにチェック
  e.今回はVMnet19を使用するため、VMnet19を選択

f:id:wiasth:20141227012613p:plain


  f.設定が終わったら下の方にあるOKボタンを押し反映させる
 
 ・Lubuntuについてもa~fを行なう

 

 ・CentOS側からIPアドレスの設定を行なう
  CentOSを起動する
  システム設定ネットワーク接続を選択

  f:id:wiasth:20141227012723p:plain


  Auto eth1(もしかしたらeth0かも)を選び編集ボタンを押す これはインターネット(NAT)につなぐインターフェースなので、接続名をinternetとし適用ボタンを押す

      f:id:wiasth:20141227012746p:plain


  次にAuto eth2(もしかしたらeth1かも)を選び編集ボタンを押す これはVMnet19につなぐインターフェースなので、接続名をVMnet19とし、IPv4のセッティングタブを押す
  方式を手動に、右側にある追加ボタンを押し、IPアドレス、ネットマスク、ゲートウェイをページ一番上にあるネットワーク構成図を参考に以下のように割り振り、適用ボタンを押す

    f:id:wiasth:20141227012851p:plain


  以上でインターフェースの設定は終わったので、閉じるボタンを押し、再起動して設定を反映させる

 

 ・次にLubuntu側のIPアドレスの設定を行なう

  Lubuntuを起動する
  左下のスタートボタン設定ネットワーク接続を選択

      f:id:wiasth:20141227013003p:plain


  Wired connection1を選び編集ボタンを押す これはインターネット(NAT)につなぐインターフェースなので、接続名をinternetとし保存ボタンを押す

  この時、internetが2つできてしまうが、設定はしっかりとできているので無視してよい

    f:id:wiasth:20141227013022p:plain


  次にWired connection2を選び編集ボタンを押す これはVMnet19につなぐインターフェースなので、接続名をVMnet19とし、IPv4設定タブを押す
  方式を手動に、右側にある追加ボタンを押し、IPアドレス、ネットマスク、ゲートウェイをページ一番上にあるネットワーク構成図を参考に以下のように割り振り、保存ボタンを押す

     f:id:wiasth:20141227013055p:plain


  以上でインターフェースの設定は終わったので、閉じるボタンを押し、再起動して設定を反映させる

 

 ・pingでIP通信ができることを確認する
  CentOS側のターミナルで ping 192.1.1.2 を実行
  64 bytes from 192.1.1.2: ・・・・ と応答が返ってくれば成功
 
  次に、Lubuntu側のターミナルで ping 192.1.1.1 を実行
  64 bytes from 192.1.1.1:・・・・ と応答が返ってくれば成功

 

 ※もしうまくいかなかった場合
  CentOS、Lubuntu両方でifconfigコマンドを実行する
  これはインターフェースの情報が見られるコマンドとなる

  CentOS側:eth1の項目にinet addr:192.1.1.1があることを確認
  Lubuntu側:eth1の項目にinetアドレス:192.1.1.2があることを確認
  ⇒なければIPアドレスがしっかりと振れていない可能性があるので、ネットワーク接続設定画面を開き、設定されてなければ設定し、再起動する

 

●手順3
 概要:192.1.1.0/24内でもメールを使えるようにする

 

 ・まずはpostfixから設定

  CentOS側のターミナルで以下を実行

  su
  vi /etc/postfix/main.cf

   mynetworks = localhost
   ⇒ mynetworks = localhost, 192.1.1.0/24 に変更(自分のネットワークにVMnet19を加える)

  service postfix restart  ←postfixを再起動

 

 ・dovecotdovecot.confの編集
  vi /etc/dovecot/dovecot.conf

 

   #login_trusted_networks =
   ⇒ login_trusted_networks = 192.1.1.0/24 に変更(#をはずし、ネットワークアドレスをかく 必要ないかも?)

 

   service dovecot restart  ←dovecotを再起動

 

 以上で設定終わり

 

●手順4
 概要:CentOSとLubuntu間でのメールのやり取りをテストする

 

 ・Lubuntu側のターミナルで以下を実行
  sudo apt-get update  ←一応アップデートしておく
  sudo apt-get install evolution  ←evolutionのインストール

 

  左下にあるスタートボタンオフィスEvolutionを選択

        f:id:wiasth:20141227013525p:plain


  アカウント設定画面がでるので、以下の画面まで続行ボタンを押す
  以下の画面では、user1の情報を登録し続行ボタンを押す

     f:id:wiasth:20141227013545p:plain

 

  次の画面では、以下のようにサーバー指定欄に「192.1.1.1」を指定し、ユーザー名をuser1とする

 f:id:wiasth:20141227013601p:plain


  (次の画面:新着メールをチェックする周期は10分ぐらいにしておくといいかも)
  以下の画面で、サーバー指定欄に「192.1.1.1」を指定しておく

 f:id:wiasth:20141227013654p:plain


  アカウントの設定が完了するとパスワードをきかれるので、登録したユーザ名のパスワード(今回ならuser1のパスワード「12345」)を入力する
  次に新しいキーリングのパスワードの指定を聞かれるが、これも登録したユーザ名のパスワードで良い(と思う)

 

 ・最後にメールの送受信テストをする
  1.user1(Lubuntu側)からserver1(CentOS側)にテストメールを送る

   メールの作り方はメールサーバ内でやった時と同じなので省略
   Lubuntuのuser1アカウントでserver1へテストメールを送り、CentOSのserver1
の受信箱に来ていれば成功

 

  2.server1(CentOS側)からuser1(Lubuntu側)にテストメールを送る

   CentOSのserver1アカウントでuser1へテストメールを送り、Lubuntuのuser1の受信箱に来ていれば成功
   もしすぐ届いてないようだったら、上にある送信/受信ボタンを押せばメールがくるはず、それでもこなければevolutionを一度閉じてまた起動すればくると思う

 

 以上で、目標通り、メールクライアントを用いてローカルネットワーク内でメールのやり取りができました

 今回は、メールサーバをたてるための簡単な知識を取得するorローカルネットワーク内での検証用にメールサーバをたてるという目的を前提としているので、SSLなどのセキュリティ機能は使っていません

 ですので、社内メールサーバとして運用したり、外部にメールサーバとして公開し運用するなど、正式なものとして運用する場合はセキュリティ機能も必ず設定してください

 

 お疲れ様でした

 

 

simple-router 複数スイッチ対応 メモ (Trema 0.4.7)

こんな人向けのメモ

1.Tremaのサンプルについてくるsimple-routerの動作は理解している
2.スイッチ1つでsimple-routerの動作確認済
→スイッチ2つ以上でsimple-routerを動かしたいが、うまくいかない人

 

●テスト環境(仮想)  OpenFlow1.0
VMware
・Lubuntu 14.10
・Trema 0.4.7
・OpenvSwitch 2.3.0
(Tremaについてくる仮想ネットワークや仮想スイッチは使用していません)

 

●はじめに
 Tremaのサンプルでついてくるsimple-routerはスイッチが1つの場合を想定しているため複数スイッチではうまく動作しません
従ってソースコードに手を加える必要があります

 

●例として以下のNW図のネットワークを作成
(Pはポート番号、eth後のカッコ内はMACアドレスの末尾を示している)

f:id:wiasth:20141126193131p:plain

 

●まずはOpenvswitch端末側の設定から(必要のない方は読まなくても問題ありません)

・Openvswitch1(SW1)の設定
 ターミナルで以下を実行


 1.sudo ovs-vsctl add-br eth1
 ブリッチの設定(コントローラにつながっているインターフェース名をいれる)


 2.sudo ovs-vsctl add-port eth1 eth2
 3.sudo ovs-vsctl add-port eth1 eth3
 ブリッチにeth2とeth3をつなげる


 4.sudo ovs-ofctl show eth1
 ポート番号の割り振りを見ることができる
 Port1がeth2,Port2がeth3になっていることを確認


 5.sudo ovs-vsctl set bridge eth1 other-config:datapath-id=0000000000000001
 ※0が15個、1が1個の16ケタ
 DPID(スイッチの番号)を分かりやすい番号に変更(しておいたほうが今後楽)


 6.sudo ovs-vsctl get bridge eth1 datapath-id
 DPIDが"0000000000000001"(0が15個、1が1個の16ケタ)になっていることを確認


 7.sudo ovs-vsctl set-controller eth1 tcp:192.1.10.1:6653
 コントローラに接続(コントローラのインターフェースのIP+TCPのポート番号(6653)入れる)
 ※現在、コントローラで使用するTCPのポート番号は6653で良いそうです

 8.sudo ovs-vsctl show
 Bridgeにeth1、Portにeth1(internal),eth2,eth3がついているのを確認


 ・Openvswitch2(SW2)も同様の設定を行なう
 ただし5では
 5.sudo ovs-vsctl set bridge eth1 other-config:datapath-id=0000000000000002
 と、DPID = 2にする

 最後にsudo ovs-vsctl showで見るとSW1と同じ構成になっているはず

 

●本題のsimple-routerのソースコードいじりに入る前に

 Tremaのsimple_routerファイルを/local/ユーザー名/以下にコピー
(作業しやすくする、元ソースを残しておくため)

sudo cp -r /var/lib/gems/2.1.0/gems/trema-0.4.7/src/examples/simple_router /home/ユーザー名/

(cp -r コピーしたいディレクトリ コピー先ディレクトリ でディレクトリごとコピーできる)


●スイッチ2つ以上用のsimple-routerのソースコード
 ソースに手を加える理由は、DPID(スイッチの番号)がないと一意にエントリを決めることができない場合があるためです

 

●まずconfファイルの変更を行なう
 $interfaceと$routeにそれぞれdpidの要素を加える(以下、赤字が追加部分)

 

・$interfaceにはdpid1のport1,port2、dpid2のport1,port2をそれぞれ入力

(hwaddr(MACアドレス)はethのMACを入力)

$interface = [
{
:dpid => 1,
:port => 1,
:hwaddr => "XX:XX:XX:XX:XX:51",
:ipaddr => "192.168.1.1",
:masklen => 24
},
{
:dpid => 1,
:port => 2,
:hwaddr => "XX:XX:XX:XX:XX:5B",
:ipaddr => "192.168.10.1",
:masklen => 24
},
{
:dpid => 2,
:port => 1,
:hwaddr => "XX:XX:XX:XX:XX:A1",
:ipaddr => "192.168.2.1",
:masklen => 24
},
{
:dpid => 2,
:port => 2,
:hwaddr => "XX:XX:XX:XX:XX:AB",
:ipaddr => "192.168.10.2",
:masklen => 24
}
]

 

・$routeにはスイッチ間を行き来するためだけのテーブルをかく
 simple-router.rbのresolve_next_hopメソッドをみれば分かるが、interface変数が見つからない場合、つまり自スイッチに宛先ホストがついていない場合のみ$routeを使うのでSW-host間の情報は$routeには必要なし

 

$route = [
{
:dpid => 1,
:destination => "192.168.2.0",
:masklen => 24,
:nexthop => "192.168.10.2"
},
{
:dpid => 2,
:destination => "192.168.1.0",
:masklen => 24,
:nexthop => "192.168.10.1"
}
]

 

●interface.rb

・読み込みと初期化メソッドにdpidの要素を追加
1.class Interfaceの下
attr_reader :dpid  を追加

2.initializeメソッド
@dpid = options[:dpid]  を追加

 

・DPIDの情報がないと一意にエントリが決まらない検索用のメソッドにdpidの要素を追加

3.find_by_portメソッド
def find_by_dpid_and_port(dpid, port)  メソッド名の変更
each.dpid == dpid && each.port == port  条件の変更

4.find_by_prefixメソッド
def find_by_dpid_and_prefix(dpid, ipaddr)  メソッド名の変更
each.dpid == dpid && each.ipaddr.mask(masklen).to_s == ipaddr.mask(masklen).to_s  条件の変更

 

●routing-table.rb

・初期化メソッドのHashを3次元ハッシュにする、add,delete,lookupメソッドのキーにdpidの要素を追加

1.initializeメソッド
@db = Array.new(ADDR_LEN + 1) { Hash.new ({}) }  3次元ハッシュ化

2.addメソッド
dpid = options[:dpid]  追加
@db[dpid][masklen][prefix.to_i] = Pio::IPv4Address.new(options [:nexthop])  キーにdpidを追加

3.deleteメソッド
dpid = options[:dpid]  追加
@db[dpid][masklen].delete(prefix.to_i)  キーにdpidを追加

4.lookupメソッド

def lookup(dpid, dest)  dpidの追加
entry = @db[dpid][masklen][prefix.to_i]  キーにdpidの追加

 

●simple-router.rb

・必要な引数を追加

1.packet_inメソッド

return unless to_me?(dpid, message)  dpidを追加

2.to me?メソッド

def to_me?(dpid, message)  dpidを追加
interface = @interfaces.find_by_dpid_and_port(dpid, message.in_port)  に変更

3.handle_icmpv4_echo_requestメソッド
interface = @interfaces.find_by_dpid_and_port(dpid, message.in_port)  に変更

4.forwardメソッド
next_hop = resolve_next_hop(dpid, message.ipv4_daddr)  dpidを追加

interface = @interfaces.find_by_dpid_and_prefix(dpid, next_hop)  に変更

5.resolve_next_hopメソッド

def resolve_next_hop(dpid, daddr)  dpidを追加
interface = @interfaces.find_by_dpid_and_prefix(dpid, daddr.value)  に変更

@routing_table.lookup(dpid, daddr.value)  dpidを追加

 

●実行する場合

cd /home/ユーザー名/simple_router
sudo trema run simple-router.rb  を実行

host1側から ping 192.168.2.2
host2側から ping 192.168.1.2
を実行し、期待通り動作しているのを確認

 

●うまくいかなかった方へ
 はじめに、confファイルにミスがないか見ると良いと思います
次にメソッド名のミス、引数が足りない(特に今回追加したdpidの書き忘れ)、割り当てたIPアドレスが間違えている等を確認

 

●それでもうまくいかなければ

 どこのメソッドでエラーを吐いているか見るため、それぞれのメソッドのdefの下にinfo(printと同じです)を追加する

例 to_me?メソッドの下に info "[info]to_me?" と追加

こうすることで、パケットインで上がってきたパケットが通ったメソッドを知らせてくれるので、どこでエラーを吐いているのか分かると思います

 

●最後に

 今回は追加部分だけを記載しました(追加理由は考えれば分かると思うので)

http://uploader.83net.jp/1142100770510357133873

作成したsimple-routerはこちらでアップしています

パスは「wiasth」です(ドラッグで見れます)

 

 

 

OpenFlow Trema0.4.7 のインストール メモ

追記(2016/1月);tremaの最新バージョンが変わったようなので、0.4.7をインストールできるように説明を変えました

 

ググってもうまくできるものがなかったので記載

(OpenFlow1.0なのでもう着手する人がいない模様?)

2014/11/25現在はこの方法でいけました

環境が違う場合、この方法ではインストールできないかもしれないので参考程度にお願いします

 

・環境

VMwarePlayer

Lubuntu 14.10

 

・以下、Tremaを入れる予定の端末のターミナルで実行

 

1.sudo apt-get update

とりあえずアップデート

 

2.sudo apt-get install git gcc make ruby ruby-dev libpcap-dev libsqlite3-dev libglib2.0-dev

Tremaのインストールに必要なもののインストール

 

3.sudo gem install rubygems-update

rubygemsは-updateをつけないとうまくインストールできない模様

 

4.sudo gem update

gemにあるものをアップデート

 

5.sudo gem install trema -v 0.4.7

追記; バージョン指定オプションを使って0.4.7を指定すること

/usr/include/ruby-2.1.0/ruby/config.hがないという内容のエラーが出る(はず)

なぜかconfig.hは別の場所にインストールされているみたいなので、その場所を探す

 

6.sudo find / -name config.h

config.hは /usr/include/x86_64-linux-gnu/ruby-2.1.0/ruby/以下にあるという結果がでる(と思う)

config.hが本来あるべき場所にコピーする

 

7.sudo cp /config.hのあるディレクトリ/config.h /エラーがでたファイル/ でコピーできる(cp /config.hの後に半角スペースが入っている)

 例えば、config.hのある場所が/usr/include/x86_64-linux-gnu/ruby-2.1.0/ruby/以下で、エラーを吐いた場所が/usr/include/ruby-2.1.0/ruby/だった場合

sudo cp  /usr/include/x86_64-linux-gnu/ruby-2.1.0/ruby/config.h /usr/include/ruby-2.1.0/ruby/

 

8.コピー後、再度 sudo gem install trema -v 0.4.7 をするとうまくいく(はず)

 

9.sudo trema -v でバージョン等の情報が表示されれば成功

 

 

 

;(function(document){ var pres = document.getElementsByTagName("pre") for(var i=pres.length; i--; ){  var el = makeOl(pres[i]) pres[i].appendChild(el) } function makeOl(pre){ if (pre.className.indexOf("gist") !== -1) { return } var ol = document.createElement("ol") , li = document.createElement("li") , df = document.createDocumentFragment() , br = pre.innerHTML.match(/\n/g) || 0 ol.className = "preLine" ol.setAttribute("role", "presentation") // no lang, no line-number if( pre.className && ! /lang-./.test(pre.className) ){ br.length += 1 } for(var i=br.length; i--; ){ var li2 = li.cloneNode(true) df.appendChild(li2) } ol.appendChild(df) return ol } })(document)