FPS 0.0.3

Flow processing system (under construction).
use n::packet::*;
use n::l4;
use std::cmp::min;

alias_protocol!(extract_source, tcp, l4, source);
alias_protocol!(fill_source, tcp, l4, source);
alias_protocol!(extract_source_u16, tcp, l4, source, source_u16);
alias_protocol!(apply_source, tcp, l4, source, source_u16);

alias_protocol!(extract_destination, tcp, l4, destination);
alias_protocol!(fill_destination, tcp, l4, destination);
alias_protocol!(extract_destination_u16, tcp, l4, destination, destination_u16);
alias_protocol!(apply_destination, tcp, l4, destination, destination_u16);

extract_field!(extract_sequence, tcp, 4, sequence);
fill_field!(fill_sequence, tcp, 4, sequence);
extract_field_value!(extract_sequence_u32, tcp, sequence, u32);
apply_field_value!(apply_sequence, tcp, sequence, u32);

extract_field!(extract_acknowledgment, tcp, 4, acknowledgment);
fill_field!(fill_acknowledgment, tcp, 4, acknowledgment);
extract_field_value!(extract_acknowledgment_u32, tcp, acknowledgment, u32);
apply_field_value!(apply_acknowledgment, tcp, acknowledgment, u32);

extract_field!(extract_offset_flags, tcp, 2, offset_flags);
fill_field!(fill_offset_flags, tcp, 2, offset_flags);

extract_field!(extract_window, tcp, 2, window);
fill_field!(fill_window, tcp, 2, window);
extract_field_value!(extract_window_u16, tcp, window, u16);
apply_field_value!(apply_window, tcp, window, u16);

extract_field!(extract_checksum, tcp, 2, checksum);
fill_field!(fill_checksum, tcp, 2, checksum);
extract_field_value!(extract_checksum_u16, tcp, checksum, u16);
apply_field_value!(apply_checksum, tcp, checksum, u16);

extract_field!(extract_urgent, tcp, 2, urgent);
fill_field!(fill_urgent, tcp, 2, urgent);
extract_field_value!(extract_urgent_u16, tcp, urgent, u16);
apply_field_value!(apply_urgent, tcp, urgent, u16);

#[millefeuille]
pub fn extract_header_length_u8(packet: Packet) -> Packet {
    let mut packet = packet;
    let offset;
    {
        let field = packet.context.get_field("tcp::offset_flags");
        offset = field.read_u8(0) >> 4;
    }
    packet.context.set_u8("tcp::header_length_u8", offset << 2);
    packet
}

#[millefeuille]
pub fn apply_header_length(packet: Packet) -> Packet {
    let mut packet = packet;
    let mut field = packet.context.take_field("tcp::offset_flags");
    let value = packet.context.get_u8("tcp::header_length_u8");
    let value = ((value >> 2) << 4) & 0xF0u8;
    let value = value | (field.read_u8(0) & 0x01u8);
    field.write_u8(0, value);
    packet.context.set_field("tcp::offset_flags", field);
    packet
}

#[millefeuille]
pub fn extract_flags_u16(packet: Packet) -> Packet {
    let mut packet = packet;
    let flags;
    {
        let field = packet.context.get_field("tcp::offset_flags");
        flags = field.read_u16(0) & 0x01FF;
    }
    packet.context.set_u16("tcp::flags_u16", flags);
    packet
}

#[millefeuille]
pub fn apply_flags(packet: Packet) -> Packet {
    let mut packet = packet;
    let mut field = packet.context.take_field("tcp::offset_flags");
    let value = packet.context.get_u16("tcp::flags_u16");
    let value = value & 0x01FF;
    let value = value | (((field.read_u8(0) & 0xFE) as u16) << 8);
    field.write_u16(0, value);
    packet.context.set_field("tcp::offset_flags", field);
    packet
}



#[millefeuille]
pub fn prepare_option_parsing(packet: Packet) -> Packet {
    let mut packet = packet;
    let tcp_hlen = packet.context.get_field("tcp::offset_flags").read_u8(0);
    let tcp_hlen = (tcp_hlen >> 4) << 2;
    assert!(tcp_hlen >= 20);
    packet.context.set_u8("tcp::remaining_options_u8", tcp_hlen - 20);

    packet
}

#[millefeuille]
pub fn prepare_option_filling(packet: Packet) -> Packet {
    let mut packet = packet;
    packet.context.set_u8("tcp::current_options_u8", 0);

    packet
}

#[millefeuille]
pub fn has_more_options(packet: Packet) -> Packet {
    let mut packet = packet;
    let remaining = packet.context.get_u8("tcp::remaining_options_u8");
    packet.context.set_bool_released("tcp::more_options_bool", remaining > 0);

    packet
}

#[millefeuille]
pub fn extract_current_option(packet: Packet) -> Packet {
    let mut remaining = packet.context.get_u8("tcp::remaining_options_u8");
    assert!(remaining > 0);
    let (mut packet, kind) = packet.right_packet(1);
    remaining = remaining - 1;
    match kind.read_u8(0) {
        0 => {
            packet.context.set_u8_released("tcp::remaining_options_u8", 0);
            packet.right_strip(remaining as usize)
        } //end of list
        1 => {
            packet.context.set_u8_released("tcp::remaining_options_u8", remaining);
            packet
        } //no-op
        _ => {
            let (mut packet, size_field) = packet.right_packet(1);
            remaining = remaining - 1;
            let size = min(size_field.read_u8(0) - 2, remaining); //kind and size itself
            remaining = remaining - size;

            packet.context.set_u8_released("tcp::remaining_options_u8", remaining);
            packet.right_strip(size as usize)
        } //end of list
    }
}

#[millefeuille]
pub fn verify_header_checksum(packet: Packet) -> Packet {
    let mut packet = packet;
    let payload_length = min(packet.len(),
                             packet.context.get_u32("ip::payload_length_u32") as usize);
    let mut sum = packet.ones_complement_sum_u16(payload_length) as u32;
    sum = sum + packet.context.get_u16("ip::pseudo_csum_u16") as u32;
    sum = (sum >> 16) + (sum & 0x0000FFFF);
    let result = sum as u16;
    packet.context.set_u16("tcp::current_csum_u16", result);
    packet.context.set_bool("tcp::correct_csum_bool",
                            (result == 0xFFFF) || (result == 0x0000));
    packet
}

#[millefeuille]
pub fn apply_current_checksum(packet: Packet) -> Packet {
    let mut packet = packet;
    let mut field = packet.context.take_field("tcp::checksum");
    let current = packet.context.get_u16("tcp::current_csum_u16");
    let csum = field.read_u16(0);
    let current = (!csum) as u32 + current as u32;
    let current = ((current >> 16) + (current & 0xFFFF)) as u16;
    field.write_u16(0, !current);
    packet.context.set_bool_released("tcp::correct_csum_bool", true);
    packet.context.set_u16_released("tcp::current_csum_u16", 0); 
    packet.context.set_field("tcp::checksum", field);
    
    packet
}

#[millefeuille]
pub fn extract(packet: Packet) -> Packet {
    let packet = verify_header_checksum(packet);
    let packet = extract_source(packet);
    let packet = extract_destination(packet);
    let packet = extract_sequence(packet);
    let packet = extract_acknowledgment(packet);
    let packet = extract_offset_flags(packet);
    let packet = extract_window(packet);
    let packet = extract_checksum(packet);
    let packet = extract_urgent(packet);

    let packet = prepare_option_parsing(packet);

    let mut packet = packet;
    loop {
        packet = has_more_options(packet);
        if !packet.context.get_bool("tcp::more_options_bool") {
            break;
        }
        packet = extract_current_option(packet);
    }

    packet
}

#[millefeuille]
pub fn fill(packet: Packet) -> Packet {
    let packet = prepare_option_filling(packet);
    let packet = fill_urgent(packet);
    let packet = fill_checksum(packet);
    let packet = fill_window(packet);
    let packet = fill_offset_flags(packet);
    let packet = fill_acknowledgment(packet);
    let packet = fill_sequence(packet);
    let packet = fill_destination(packet);
    let packet = fill_source(packet);

    packet
}