0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rust で始める(低レイヤー?)Network Programing

Last updated at Posted at 2023-01-09

Rustを使ったネットワークプログラミングを書いていきます。
ネットワーク、セキュリティの勉強のために低レイヤー(L2)からパケットを扱うプログラムを書いていきます。
webサーバーなどを実装したい人は、portをバインディングする書き方で十分です。

前提環境

・Rustがインストール済みのLinux
・管理者権限を得られる環境(Network インターフェイスを利用するため)
・L2スイッチ

Network知識の習得のためなので、参考に動かす方は隔離環境を用意して実行してください。

pnet の使い方について

全てのecho Request(ping) に反応するプログラム

実行時の引数にインターフェイス名を指定すると、そのインターフェイスに来たecho request に対するecho replyを返す。
インターネットへ到達不能なネットワークのホストからインターネット上のホストへのpingが通るように見える。

ネットワーク構成図.png

About pnet

// 作成中 ...

プログラム全体

DummyReply.src
extern crate pnet;

use pnet::datalink;
use pnet::datalink::Channel::Ethernet;
use pnet::datalink::DataLinkSender;
use pnet::datalink::NetworkInterface;
use pnet::packet::ethernet::EtherTypes;
use pnet::packet::ethernet::EthernetPacket;
use pnet::packet::ethernet::MutableEthernetPacket;
use pnet::packet::icmp;
use pnet::packet::icmp::*;
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4;
use pnet::packet::ipv4::Ipv4Packet;
use pnet::packet::Packet;
use pnet::util::MacAddr;
use std::env;
use std::net::Ipv4Addr;


fn icmp_packet_reply(
    data: &[u8],
    ipv4packetinfo: IPv4PacketInfo,
    tx: &mut Box<dyn DataLinkSender>,
) {
    // construct icmp packet
    let mut raw_packet: Vec<u8> = vec![0; icmp::MutableIcmpPacket::minimum_packet_size() + data.len()];
    let mut mipacket: MutableIcmpPacket = icmp::MutableIcmpPacket::new(&mut raw_packet).unwrap();
    mipacket.set_payload(data);
    mipacket.set_icmp_type(IcmpTypes::EchoReply);
    mipacket.set_icmp_code(IcmpCode::new(0));
    mipacket.set_checksum(0 as u16);
    mipacket.set_checksum(icmp::checksum(&mipacket.to_immutable()));


    // construct ipv4 packet
    let mut raw_packet: Vec<u8> = vec![0; ipv4::MutableIpv4Packet::minimum_packet_size() + mipacket.packet().len()];
    let mut mi4packet: ipv4::MutableIpv4Packet = ipv4::MutableIpv4Packet::new(&mut raw_packet).unwrap();
    mi4packet.set_version(4); /* version */
    mi4packet.set_header_length(ipv4::Ipv4Packet::minimum_packet_size() as u8); /* Internet Header Length */
    mi4packet.set_header_length(5); /* Internet Header Length */
    mi4packet.set_ecn(24); /* Type of Service */
    mi4packet.set_dscp(24); /* Type of Service */
    mi4packet.set_total_length(mi4packet.packet().len() as u16); /* Total Length */
    mi4packet.set_identification(0); /* Identification */
    mi4packet.set_flags(0); /* Various Control Flags */
    mi4packet.set_fragment_offset(0); /* Fragment Offset */
    mi4packet.set_ttl(115); /* Time to Live */
    mi4packet.set_next_level_protocol(IpNextHeaderProtocols::Icmp); /* Protocol */
    mi4packet.set_checksum(0); /* Header Checksum */
    mi4packet.set_source(ipv4packetinfo.destination); /* Source Address */
    mi4packet.set_destination(ipv4packetinfo.source); /* Destination Address */
    mi4packet.set_checksum(ipv4::checksum(&mi4packet.to_immutable())); /* Header Checksum */
    mi4packet.set_payload(&mut mipacket.packet());


    // construct L2packet
    let mut raw_packet: Vec<u8> = vec![0; MutableEthernetPacket::minimum_packet_size() + mi4packet.packet().len()];
    let mut mepacket = MutableEthernetPacket::new(&mut raw_packet).unwrap();
    mepacket.set_payload(mi4packet.packet());

    mepacket.set_destination(ipv4packetinfo.ehternet_packet_info.as_ref().unwrap().source);
    mepacket.set_source(ipv4packetinfo.ehternet_packet_info.as_ref().unwrap().destination);
    mepacket.set_ethertype(EtherTypes::Ipv4);


    // reply
    tx.send_to(mepacket.packet(), None);
}


// Layer 3
struct IPv4PacketInfo {
    // Layer 3
    source: Ipv4Addr,
    destination: Ipv4Addr,
    ehternet_packet_info: Option<EthernetPacketInfo>
}
// Layer 2
struct EthernetPacketInfo {
    source: MacAddr,
    destination: MacAddr,
}


fn ethernet_packet_check(
    ethernet_packet: &EthernetPacket,
    tx: &mut Box<dyn DataLinkSender>,
){
    if ethernet_packet.get_ethertype() == EtherTypes::Ipv4 {
        let ethernetpacketinfo = EthernetPacketInfo {
            source: ethernet_packet.get_source(),
            destination: ethernet_packet.get_destination(),
        };


        let ipv4packet = Ipv4Packet::new(ethernet_packet.payload()).unwrap();
        if ipv4packet.get_next_level_protocol() == IpNextHeaderProtocols::Icmp {
            let ipv4packetinfo: IPv4PacketInfo = IPv4PacketInfo {
                source: ipv4packet.get_source(),
                destination: ipv4packet.get_destination(),
                ehternet_packet_info: Some(ethernetpacketinfo),
            };
            
            
            let icmppacket: IcmpPacket = IcmpPacket::new(ipv4packet.payload()).unwrap();
            if icmppacket.get_icmp_type() == IcmpType(8) {
                println!("catch Echo Request : {} -> {}", ipv4packet.get_source(), ipv4packet.get_destination());
                icmp_packet_reply(
                    icmppacket.payload(),
                    ipv4packetinfo,
                    tx,
                );
            }
        }
    }
}


fn main() {
    // set interface
    let mut interface_name: &str = "interface name";
    let args: Vec<String> = env::args().collect();
    if args.len() > 1 {
        interface_name = args[1].as_str();
    }

    let interfaces: Vec<NetworkInterface> = datalink::interfaces()
        .into_iter()
        .filter(|interface: &NetworkInterface| interface_name == interface.name.as_str() )
        .collect();
    
    if interfaces.len() == 0 {
        println!("Interface is Not Found.");
        return;
    }
    let interface: &NetworkInterface = &interfaces[0];


    let (mut tx, mut rx) = datalink::channel(&interface, Default::default())
        .map(|ch| match ch {
            Ethernet(tx, rx) => (tx, rx),
            _ => panic!("Error occured."),
        })
        .unwrap();

    loop {
        match rx.next() {
            Ok(src) => {
                let ehternet_packet = &EthernetPacket::new(src).unwrap();
                ethernet_packet_check(ehternet_packet, &mut tx);
            }
            Err(_) => {
                println!("Error occured in loop.");
                break;
            }
        }
    }
}

pnet のバージョンを0.29.0で固定する。
※最新にするとdatalinkモジュールが削除されるのでインターフェイスを利用するために別のライブラリが必要になるため。

cargo.toml
[dependencies]
pnet = "0.29.0"
0
2
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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?