totofugaのブログ

ネットワークとかc言語、perlの話。

ソースルーティング

IPのオプションを指定してLinuxで経路を指定してパケットを出す方法を調べてみた

設定方法

IPのオプションを指定する方法は以下の2通りがある

  • ソケット単位に指定する setsockoptでIP_OPTIONSを指定する

  • パケット単位で設定を変える sendmsgのstruct msghdr->msg_controlにIP_RETOPTのCMSGを設定する

今回はソケット単位に指定する方法を使用

ソース

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/udp.h>

#include <linux/ip.h>

void main() {
    unsigned char options[256] = {0};
    options[0] = IPOPT_LSRR; // type
    int len = 11;
    options[1] = len; // len;
    options[2] = 4; // pointer

    struct in_addr addr;
    inet_aton("1.1.1.1", &addr);
    *((uint32_t*)(options + 3))  = addr.s_addr;

    inet_aton("2.2.2.2", &addr);
    *((uint32_t*)(options + 7))  = addr.s_addr;
    options[7] = IPOPT_NOOP;

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if ( sock == -1 ) {
        perror("creat socket error\n");
        exit(1);
    }

    if ( setsockopt(sock, SOL_IP, IP_OPTIONS, options, len) == -1 ) {
        perror("set ip options error\n");
        exit(1);
    }

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8080);
    inet_aton("3.3.3.3", &sin.sin_addr);

    char buf[] = "test";
    int ret = sendto(sock, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin));
    printf("%d\n", ret);
}

動作

ソースルーティングはoptionで指定されているものが送信先となる。 この場合1.1.1.1 => 2.2.2.2 => 3.3.3.3という経路になるので 送信先が1.1.1.1となる。 sendで指定されたものは送信先ではなく、経路の最後に追加される。

処理しているのはLinuxカーネルのnet/ipv4/ip_options.c:ip_options_compile周り

ルータの設定

デフォルトではLinuxをルータとして使用している場合ip_forwardを有効にしてもソースルーティングは有効にならない。

echo "1" > /proc/sys/net/ipv4/conf/all/accept_source_route 

を実行して使用したいデバイス

echo "1" > /proc/sys/net/ipv4/conf/<dev>/accept_source_route

sourceルーティングを有効にしておく必要がある