読者です 読者をやめる 読者になる 読者になる

Last.fm APIのgolangラッパーを作った

go

修論の合間に書きはじめて放置していたので一通り書き上げた。
https://github.com/shkh/lastfm-go

一昔前のLast.fmには邦楽アーティストの情報があまりなくて使えない感じだったけど、最近はかなり充実してきていて、アーティストの情報やアルバムの楽曲リストを取ってくるにはかなり良い選択肢だと思う。提供されてるAPIが幅広くて、いろんな統計情報も取ってこれる。

たとえば最近気合入ってるアーティストの一覧とか。

api := lastfm.New(APIKEY, SECRET)
result, _ := api.Chart.GetHypedArtists(nil)
for _, artist := range result.Artists {
    fmt.Println(artist.Name)
}
East India Youth
Blood Cultures
Jennifer Nettles
Painted Palms
7Horse
Luke Bond
Syn Cole
Kwabs
Kevin Drew
Raleigh Ritchie
・
・
・

全部知らん。

認証はモバイルアプリ向けとデスクトップアプリ向け、ウェブアプリ向けの3通りがあって、適当に選んで認証する。たとえばモバイルアプリ向けは

err = api.Login(username, password)

という感じ。

認証したら禊は完了、ShoutしたりScrobbleしたり色々遊べる。

err = api.User.Shout(lastfm.P{"user": "shkh_", "message": "ʕ ◔ϖ◔ʔ"})

Scrobbleするには、Track.UpdateNowPlayingとTrack.Scrobbleを使う。

start := time.Now().Unix()
p := lastfm.P{"artist": artist, "track": track}
_, err = api.Track.UpdateNowPlaying(p) //プロフィールに再生中のアイコンが表示される
if err != nil {
    return
}
time.Sleep(35 * time.Second) //ちょっと待たないといけない
p["timestamp"] = start
_, err = api.Track.Scrobble(p)
if err != nil {
    return
}

ns3でパケットのトレース

ns3 C++

ns3ではデータグラムやパケット、フレームは、すべてPacketオブジェクトになる。

各レイヤでそのPacketオブジェクトを受信した時や破棄した時などに、イベントを発生させるようになっていて、あらかじめ指定しておいたコールバック関数を呼び出してくれる。そこで、殆どのイベントはPacketオブジェクトのポインタをコールバック関数に渡してくれるので、各レイヤでPacketオブジェクトの中身を見ることが出来る。イベントがなくても簡単に追加できる。

そこでイベント発生時に、パケットに含まれる特定のヘッダの特定のフィールドの値で処理を分岐したいということが多々ある。このとき、どのレイヤでトレースするかによって、Packetオブジェクトのヘッダとペイロードが決まるので、目的のヘッダが見つかるまで順番にヘッダを取り除いて (パケットをコピーしてからRemoveHeaderメソッドを呼ぶ) 行けば良い。上位レイヤにパケットを渡すときも同様の処理が行われている。

WifiMacHeader header_mac;
pkt->RemoveHeader (header_mac);

Ipv4Header header_ip;
pkt->RemoveHeader (header_ip);

.
.
.

ここで面倒なのは、各プロトコルのヘッダクラスが、抽象クラスのHeaderクラスを継承して定義されていて基底クラスでインスタンス化できないので、派生クラス (Ipv4Headerなど) のオブジェクト参照を渡す必要があるというところ。そうするとトレースするときに、パケットに含まれているプロトコルの種類とその順番が既知である必要がある。多分そう。上位レイヤでは、どんなヘッダが付いているかある程度予測することができるので、この方法でもなんとかなる。これがリンクレイヤなどの下位レイヤになると、制御メッセージなどが氾濫するので、この方法では難しくなる。

そういうときはPacketMetadataを使う。PacketMetadataはパケットの中に含まれる複数のプロトコルのヘッダとペイロード、トレイラを連結リストで保持しているので、これをイテレートして目的のヘッダを見つける。たとえばIPv4ヘッダを見つけるには以下のようにする。

PacketMetadata::ItemIterator it = pkt->BeginItem ();
PacketMetadata::Item item;
while (it.HasNext ())
{
    item = it.Next ();
    if (item.type == PacketMetadata::Item::HEADER)
    {
        if (item.tid.GetName () == "ns3::Ipv4Header")
        {
            NS_LOG_INFO ("IPv4 header found.");
        }
     }
}

item.typeが、種別を表す列挙型 (enum {PAYLOAD, HEADER, TRAILER}) の変数になっている。

中身はシリアライズされているので、ヘッダのフィールドにアクセスするにはデシリアライズしてやればいい。

Ipv4Header header;
header.Deserialize (item.current);

Ipv4Address src = header.GetSource ();
Ipv4Address dst = header.GetDestination ();

というような感じで、ペイロードやトレイラの場合も、その必要があるかどうかは別として、同様にすればとりあえず判別出来る。このPacketMetadata経由でヘッダやトレイラの追加・削除、シリアライズ・デシリアライズすることもできるので、パケットを操作するときはこれを使うと良さそう。

参考
http://www.nsnam.org/doxygen/classns3_1_1_packet.html
http://www.nsnam.org/doxygen/classns3_1_1_packet_metadata.html
http://www.nsnam.org/docs/manual/html/tracing.html#using-the-config-subsystem-to-connect-to-trace-sources

ns3で全ノードの経路表を集約する

ns3 C++

ネットワークシミュレータのns3で、任意のノードにおいてネットワーク全体の完全な経路表が欲しくて、APIあると思ったけどなさそうだった。あるかもしれない。具体的には以下の様なデータ構造が欲しかった。

{
    [ 送信元アドレス, 宛先アドレス ] : [ 中継ノードのアドレス, ... ],
                    .
                    .
                    .
}

今使ってるグラフ理論のライブラリが、この形式で出力してくれるので、同じ形式で保持したい。

以下がコードの断片。ここではL3のルーティングプロトコルはOLSRを使ってる。プロアクティブ型の場合は知らん。OLSRで各ノードが保持するのは (宛先アドレス, ホップ数, インターフェースID, ネクストホップアドレス) の組なので、上のデータ構造をつくるためには全ノードの経路表を覗く必要がある。

typedef std::map<std::pair<Ipv4Address, Ipv4Address>, std::vector<Ipv4Address> > Tables; 
typedef std::map<std::pair<Ipv4Address, Ipv4Address>, Ipv4Address> NextHops;
Tables tables;
NextHops next_hops;

for (uint32_t i=0; i<NodeList::GetNNodes (); ++i)
{
    Ptr<Node> node = NodeList::GetNode (i);
    Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
    Ptr<Ipv4ListRouting> list = DynamicCast<Ipv4ListRouting> (ipv4->GetRoutingProtocol ());
    for (uint32_t j=0; j<list->GetNRoutingProtocols (); ++j)
    {
        int16_t priority;
        Ptr<Ipv4RoutingProtocol> anonymous = list->GetRoutingProtocol (j, priority);
        if (DynamicCast<olsr::RoutingProtocol> (anonymous))
        {
            Ptr<olsr::RoutingProtocol> olsr = DynamicCast<olsr::RoutingProtocol> (anonymous);
            std::vector<RoutingTableEntry> table = olsr->GetRoutingTableEntries ();
            for (uint32_t j=0; k<table.size (); ++k)
            {
                RoutingTableEntry entry = table[k];
                uint32_t intrf = entry.interface;
                std::pair<Ipv4Address, Ipv4Address> key;
                key.first = (ipv4->GetAddress (intrf, 0).GetLocal ());
                key.second = entry.destAddr;
                std::pair<std::pair<Ipv4Address, Ipv4Address>, Ipv4Address> elem (key, entry.nextAddr);
                next_hops.insert (elem);
            }
            break; //OLSR found
        }
    }
}

for (NextHops::iterator it = next_hops.begin (); it!=next_hops.end (); ++it)
{
    Ipv4Address src = it->first.first;
    Ipv4Address dst = it->first.second;
    Ipv4Address nxt = it->second;
    std::pair<Ipv4Address, Ipv4Address> key (src, dst);
    std::vector<Ipv4Address> intermediates;
    intermediates.push_back (nxt);
    while (nxt != dst)
    {
        nxt = next_hops[std::pair<Ipv4Address, Ipv4Address> (nxt, dst)];
        intermediates.push_back (nxt);
    }
    std::pair<std::pair<Ipv4Address, Ipv4Address>, std::vector<Ipv4Address> > elem (key, intermediates);
    tables.insert (elem);
}

for (Tables::iterator it = tables.begin (); it!=tables.end (); ++it)
{
    std::cout << "SOURCE : "<< it->first.first << ", DESTINATION : " << it->first.second << std::endl;
    std::cout << "INTERMEDIATE NODES : ";
    std::vector<Ipv4Address> intermediates = it->second;
    for (std::vector<Ipv4Address>::iterator addr=intermediates.begin (); addr!=intermediates.end (); ++addr)
    {
        std::cout << *addr << " ";
    }
    std::cout << std::endl << std::endl;
}

たとえば下みたいなのが出る。中継ノードの最後は宛先ノードで終わるようにした。これで中継ノード数がホップ数になる。

SRC : 10.1.1.1, DST : 10.1.1.2
INTERMEDIATE NODES : 10.1.1.96 10.1.1.30 10.1.1.27 10.1.1.34 10.1.1.18 10.1.1.51 10.1.1.69 10.1.1.25 10.1.1.2 

SRC : 10.1.1.1, DST : 10.1.1.3
INTERMEDIATE NODES : 10.1.1.96 10.1.1.30 10.1.1.27 10.1.1.34 10.1.1.18 10.1.1.51 10.1.1.95 10.1.1.20 10.1.1.12 10.1.1.55 10.1.1.40 10.1.1.59 10.1.1.70 10.1.1.3 

SRC : 10.1.1.1, DST : 10.1.1.4
INTERMEDIATE NODES : 10.1.1.96 10.1.1.30 10.1.1.27 10.1.1.34 10.1.1.18 10.1.1.51 10.1.1.69 10.1.1.25 10.1.1.92 10.1.1.29 10.1.1.98 10.1.1.58 10.1.1.4 

SRC : 10.1.1.1, DST : 10.1.1.5
INTERMEDIATE NODES : 10.1.1.96 10.1.1.30 10.1.1.5 

SRC : 10.1.1.1, DST : 10.1.1.6
INTERMEDIATE NODES : 10.1.1.96 10.1.1.30 10.1.1.27 10.1.1.34 10.1.1.18 10.1.1.51 10.1.1.69 10.1.1.25 10.1.1.6 

SRC : 10.1.1.1, DST : 10.1.1.7
INTERMEDIATE NODES : 10.1.1.96 10.1.1.30 10.1.1.27 10.1.1.34 10.1.1.18 10.1.1.51 10.1.1.69 10.1.1.25 10.1.1.92 10.1.1.29 10.1.1.98 10.1.1.7 

(略)

配置は下みたいな感じ。

http://farm4.staticflickr.com/3757/10066465633_6ea8ffede9_c.jpg

注意点として、経路が収束するのに結構時間がかかるので、最低でも30チックくらいは待ってから実行した方がいい。ノード数100くらいになると多分足りなくて、60チックくらいにしてる。
もちろん静的配置の場合で。それから、上のコードの実行にもかなり時間がかかるから、やっぱり移動性あると厳しいと思う。