ルートテーブルの取得するクラスの作成

インターフェース情報を取得できたので、同じIPHELPAPIを使ってルーティング情報を取得するクラスを作ることにしました。
ルーティング情報は、"route print"をコマンド実行して結果の文字列を処理して取得しないとファイルの一覧のようにWindowsAPIでは取得できなかったので、今後の利用も考慮して作ることに・・・。


C++の基本プログラムとして、RouteInfoクラスを作成することにしました。
RouteInfo.h

#ifndef _ROUTE_INFO_CLASS
#define _ROUTE_INFO_CLASS

#include <windows.h>
#include <tchar.h>
#include <comdef.h>

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
#define DIRECT_ROUTE        MIB_IPROUTE_TYPE_DIRECT
#define INDIRECT_ROUTE      MIB_IPROUTE_TYPE_INDIRECT

#include <vector>
#include <string>

#include "addrinfo.h"

typedef basic_string<_TCHAR>    String;

class RouteInfo;
typedef vector<RouteInfo>       route_list;

class RouteInfo {
public:
    RouteInfo(const RouteInfo& info);
    RouteInfo(String dest, String mask, String gateway, unsigned long index, unsigned long metric, bool defaultGw);
    RouteInfo();
    ~RouteInfo();

    String              getDest();
    String              getMask();
    String              getGateway();
    unsigned long       getIndex();
    unsigned long       getType();
    unsigned long       getMetric();
    bool                isDefaultGateway();
    bool                isPrimaryGateway();
    bool                isSameRouteInfo(String dest, String mask, String gateway);

    static route_list       getRouteList();

private:
    String          dest;
    String          mask;
    String          gateway;
    unsigned long   index;
    unsigned long   type;
    unsigned long   metric;
    bool            defaultGw;
    bool            primaryGw;
};

#endif  //  _ROUTE_INFO_CLASS

プログラム本体は、
RouteInfo.cpp

#include "routeinfo.h"

//  Copy Constructor
RouteInfo::RouteInfo(const RouteInfo& info) {
    dest = info.dest;
    mask = info.mask;
    gateway = info.gateway;
    index = info.index;
    type = info.type;
    metric = info.metric;
    defaultGw = info.defaultGw;
    primaryGw = info.primaryGw;
}

//  Constructor
RouteInfo::RouteInfo(String dest, String mask, String gateway, unsigned long index, unsigned long metric, bool defaultGw) {
    this->dest = dest;
    this->mask = mask;
    this->gateway = gateway;
    this->index = index;
    this->type = type;
    this->metric = metric;
    this->defaultGw = defaultGw;
    this->primaryGw = false;
}

//  Constructor
RouteInfo::RouteInfo() {
    index = 0;
    type = DIRECT_ROUTE;
    metric = 0;
    defaultGw = false;
    primaryGw = false;
}

//  Destructor
RouteInfo::~RouteInfo() {
}

/*
 *  あて先アドレスを取得する
 *  param   : 無し
 *
 *  return  : String
 */
String RouteInfo::getDest() {
    return dest;
}

/*
 *  ネットマスクを取得する
 *  param   : 無し
 *
 *  return  : String
 */
String RouteInfo::getMask() {
    return mask;
}

/*
 *  ゲートウエイアドレスを取得する
 *  param   : 無し
 *
 *  return  : String
 */
String RouteInfo::getGateway() {
    return gateway;
}

/*
 *  インデックスを取得する
 *  param   : 無し
 *
 *  return  : unsigned long
 */
unsigned long RouteInfo::getIndex() {
    return index;
}

/*
 *  タイプを取得する
 *  param   : 無し
 *
 *  return  : unsigned long
 */
unsigned long RouteInfo::getType() {
    return type;
}

/*
 *  メトリックを取得する
 *  param   : 無し
 *
 *  return  : unsigned long
 */
unsigned long RouteInfo::getMetric() {
    return metric;
}

/*
 *  デフォルトゲートウエイかどうかを取得する
 *  param   : 無し
 *
 *  return  : bool
 *            true  = デフォルトゲートウエイである
 *            false = デフォルトゲートウエイでない
 */
bool RouteInfo::isDefaultGateway() {
    return defaultGw;
}

/*
 *  プライマリゲートウエイかどうかを取得する
 *  param   : 無し
 *
 *  return  : bool
 *            true  = プライマリゲートウエイである
 *            false = プライマリゲートウエイでない
 */
bool RouteInfo::isPrimaryGateway() {
    return primaryGw;
}

/*
 *  同じルート情報か確認する
 *  param   : あて先アドレス(String)
 *            ネットマスク(String)
 *            ゲートウエイアドレス(String)
 *
 *  return  : bool
 *            true  = 同じルート情報である
 *            false = 同じルート情報でない
 */
bool RouteInfo::isSameRouteInfo(String dest, String mask, String gateway) {
    if (this->dest != dest)
        return false;
    if (this->mask != mask)
        return false;
    if (this->gateway != gateway)
        return false;
    return true;
}

/*
 *  ルート情報の一覧を取得する
 *  param   : 無し
 *
 *  return  : route_list
 */
route_list RouteInfo::getRouteList() {
    route_list rt_list;

    DWORD lowest_metric = ~0;
    int pgw = -1;

    DWORD dwSize = 0;
    GetIpForwardTable(NULL, &dwSize, false);
    PMIB_IPFORWARDTABLE pForwardInfo = (MIB_IPFORWARDTABLE *)calloc(dwSize, 1);
    if (GetIpForwardTable(pForwardInfo, &dwSize, false) == NO_ERROR) {
        for (unsigned long i = 0; i < pForwardInfo->dwNumEntries; i++) {
            MIB_IPFORWARDROW *row = &pForwardInfo->table[i];
            RouteInfo route_entry = RouteInfo();
            route_entry.dest = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&row->dwForwardDest)));
            route_entry.mask = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&row->dwForwardMask)));
            route_entry.gateway = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&row->dwForwardNextHop)));
            route_entry.index = row->dwForwardIfIndex;
            route_entry.type = row->dwForwardType;
            route_entry.metric = row->dwForwardMetric1;
            if (!row->dwForwardDest && !row->dwForwardMask) {
                route_entry.defaultGw = true;
            }
            rt_list.push_back(route_entry);
            if (route_entry.isDefaultGateway() && (lowest_metric > row->dwForwardMetric1)) {
                pgw = i;
                lowest_metric = row->dwForwardMetric1;
            }
        }
        if (pgw >= 0) {
            rt_list[pgw].primaryGw = true;
        }
        free(pForwardInfo);
    }

    return rt_list;
}

/*
 *  あて先ネットワークに最適のルート情報を取得する
 *  param   : あて先ネットワークアドレス(String)
 *            あて先ネットマスク(String)
 *            ルート情報の一覧(route_list)
 *
 *  return  : RouteInfo
 */
RouteInfo RouteInfo::getBestRoute(String dest, String mask, route_list rtList) {
    RouteInfo route_entry = RouteInfo();

    unsigned long rt_index = ~0;
    unsigned long rt_metric = ~0;
    unsigned long rt_mask = 0;

    unsigned long dest_net = inet_addr((const char *)_bstr_t(dest.c_str()));
    if (mask.empty())
        mask = HOST_MASK;
    unsigned long dest_mask = inet_addr((const char *)_bstr_t(mask.c_str()));

    if (rtList.empty()) {
        rtList = RouteInfo::getRouteList();
    }
    for (size_t i = 0; i < rtList.size(); i++) {
        RouteInfo rt = rtList.at(i);
        unsigned long route_dest = inet_addr((const char *)_bstr_t(rt.getDest().c_str()));
        unsigned long route_mask = inet_addr((const char *)_bstr_t(rt.getMask().c_str()));
        unsigned long route_base = route_dest & ((route_mask < dest_mask) ? route_mask : dest_mask);
        unsigned long cmp = dest_net & route_base;
        if (cmp == route_base) {
            if (route_mask >= rt_mask) {
                if ((route_mask > rt_mask) || (rt.getMetric() < rt_metric)) {
                    rt_index = (unsigned long)i;
                    rt_metric = rt.getMetric();
                }
                rt_mask = route_mask;
            }
        }
    }
    if (rt_index < rtList.size()) {
        route_entry = rtList.at(rt_index);
    }

    return route_entry;
}

テスト用のプログラムロジックとして今までにも使っていた、
netinfo.cppに

void getRoute() {
    route_list list = RouteInfo::getRouteList();
    for (size_t i = 0; i < list.size(); i++) {
        RouteInfo info = list.at(i);
        COUT << _T("RouteInfo ") << info.getIndex() << _T(" : ") << info.getDest() << _T("-") << info.getMask() << _T("-") << info.getGateway() << endl;
    }

    RouteInfo info = RouteInfo::getBestRoute(String(_T("202.239.113.18")));
    COUT << _T("Gateway ") << info.getIndex() << _T(" : ") << info.getDest() << _T("-") << info.getMask() << _T("-") << info.getGateway() << endl;
}

を追加して実行しました。


実はIPHLPAPIには最適なルート情報を取得するGetBestRoute()という関数が存在するんですが、システムが持っているルート情報から判断するのではなく、検証したいルーティングテーブルを自分で与えられるようにしたかったので、あえて作ってみました。