LoginSignup
12
13

More than 5 years have passed since last update.

iOSでもARPしたい!

Posted at

はじめに

iOSでもARPしたい!
そう感じられたことはありませんか?
…と思ってネットでいろいろ見ていたのですが、日本語での情報があまり見られなかったので、少し書いてみることにしました。

自分でパケットを作って、ARPしてみるのもいいのかもしれませんが、今回はFreeBSDのarpコマンドのソースコードを用いて、お手軽に行ってみようと思います。

海外の情報だと、Appleが利用しているFreeBSDのソースコードを利用するという感じでした。
コピーライトが2つ載っていて、ソースコードの又貸し状態です。

arpのソースコード

まず、FreeBSDのSVNからソースコードを入手する必要があります。
必要なのは、arp.croute.hです。
svn派ではなくgit派なので、httpでダウンロードします。
URLは次の2つになります。
https://svnweb.freebsd.org/base/stable/10/usr.sbin/arp/arp.c?view=co
https://svnweb.freebsd.org/base/stable/10/sys/net/route.h?view=co

不要な部分の削除

実際のarpコマンドはARPテーブルへの追加や削除が行えますが、実機でそれを行ったら、リジェクト直行便な気がしますので、関連する部分を念のために削除します。
また、標準出力ではなく、struct ether_addrとして取得したいので、出力用の関数なども削ってしまします。
これで、必要なヘッダーファイルを大幅に減らせます。

結局、残す関数は
static struct sockaddr_in *getaddr(char *host)
static int search(u_long addr, action_fn *action)
の2つになりました。
そしてstaticを外します。
また、search関数でstruct ether_addrのポインタを渡すように宣言と定義を変更して
struct sockaddr_in *getaddr(char *host)
int search(u_long addr, struct ether_addr *hwaddr)
となります。

定義の移動

関数を外部から使いたいので、arp.hを作成してそちらにいくつかの定義を移します。

arp.h
#include "TargetConditionals.h"

#include <net/ethernet.h>
#include <net/if_dl.h>
#if TARGET_OS_SIMULATOR
#include <net/route.h>
#else
#include "route.h"
#endif

typedef void (action_fn)(struct sockaddr_dl *sdl,
        struct sockaddr_in *s_in, struct rt_msghdr *rtm);

struct sockaddr_in *getaddr(char *host);
int search(u_long addr, struct ether_addr *hwaddr);

実は、route.hはiOS SimulatorのSDKには含まれており、自前で用意するとコンパイルが通らなくなるので、定数で分岐します。
分岐に用いるTARGET_OS_SIMULATORTargetConditionals.hで定義されているので、忘れずに#includeしてあげてください。

ソースコードの修正

arp.hで、先ほどダウンロードしたroute.h#includeしてますが、iOSには無いヘッダーファイルを参照しているので、修正します。
まず

route.h
#include <sys/counter.h>

route.h
#include <sys/socket.h>
#include <sys/types.h>

に変更します。
続いて、中ほどにある

route.h
#include <net/radix.h>

を削除します。不要です。

次に、arp.cに戻り、不要な#includeと定義を削除します。
残す#include

arp.c
#include <sys/sysctl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>

の6つになりました。
必要な定義はarp.hに移しましたので、ほかの定義はばっさり全削除です。
スマートです。

また、iOS SimulatorのSDKにはSA_SIZEの定義がないと怒られるので、FreeBSDのroute.hを参考にしてarp.cに追加しておきます。

arp.c
#ifndef SA_SIZE
#define SA_SIZE(sa) \
    ((!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
    sizeof(long) : \
    1 + ((((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1)))
#endif

最後に、関数本体の修正を行います。
まず、エラー出力を取り除きます。
warnx関数の呼び出しを削除し、err関数の呼び出しをreturn 0などに置換します。
インタフェースの指定などはしないのでsearch関数から、

arp.c
if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
    strcmp(ifname, rifname))
    continue;

のコードとchar ifname[IF_NAMESIZE];の定義を削除します。
また、search関数ではstruct ether_addrを返すので、

arp.c
(*action)(sdl, sin2, rtm);

arp.c
memcpy(hwaddr, LLADDR(sdl), sdl->sdl_alen);

と修正します。

使い方

あとは、ARPしたいコードで、関数定義を行ったarp.h#includeして呼び出すだけです。
ついでに、文字列からin_addr_tを生成してくれるinet_addr関数を定義したarpa/inet.h#includeすると便利です。

IPアドレスが格納された文字列変数ipから、MACアドレスを格納した文字列変数macを取得するコードは次のようになります。

i_want_to_arp_on_ios.m
struct ether_addr hw_addr;
if (!search(inet_addr([ip cStringUsingEncoding:NSASCIIStringEncoding]), &hw_addr)) {
    return nil;
}
NSMutableString *mac = [[NSMutableString alloc] init];
for (int i = 0; i < sizeof(hw_addr.octet); i++) {
    if (i > 0) {
        [mac appendString:@":"];
    }
    [mac appendFormat:@"%02X", hw_addr.octet[i]];
}

arp.cには関数getaddrも残してあるので、ホスト名を解決してMACアドレスを参照するといったこともできます。

さいごに

FreeBSDの4条項BSDライセンスに注意して利用してください。
この記事で示したコードはご自由に使っていただいて大丈夫です。ただし、一切の責任を取りかねますのでご了承ください。

12
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
13