aboutsummaryrefslogtreecommitdiff
path: root/src/fw.rs
blob: aabc420aba44e54155d382d37e1e9e405bd80d62 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use nftnl::{nft_expr, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table};
use std::{
    ffi::{self, CString},
    io,
};
use mnl;
use libc;

const TABLE_NAME: &str = "diplonat";
const OUT_CHAIN_NAME: &str = "out";
const IN_CHAIN_NAME: &str = "in";

#[derive(Debug)]
struct Error(String);

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error(error.to_string())
    }
}

impl From<ffi::NulError> for Error {
    fn from(error: ffi::NulError) -> Self {
        Error(error.to_string())
    }
}

fn send_and_process(batch: &FinalizedBatch) -> Result<(), Error> {
    let socket = mnl::Socket::new(mnl::Bus::Netfilter)?;
    socket.send_all(batch)?;

    let portid = socket.portid();
    let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize];

    while let Some(message) = socket_recv(&socket, &mut buffer[..])? {
        match mnl::cb_run(message, 2, portid)? {
            mnl::CbResult::Stop => {
                break;
            }
            mnl::CbResult::Ok => (),
        }
    }
    Ok(())
}

fn socket_recv<'a>(socket: &mnl::Socket, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> {
    let ret = socket.recv(buf)?;
    if ret > 0 {
        Ok(Some(&buf[..ret]))
    } else {
        Ok(None)
    }
}

fn add_port_allowed(port: u16) -> Result<(), Error> {
    let mut batch = Batch::new();

    // TODO: at the moment, I haven't found a way to properly separate setup part (create table and
    // chains) and the add rule part because the add rule part needs a reference on the chains.
    // apparently creating a table and chains that already exist seems to do nothing so it works
    // doing the following. To be done properly though
    
    let table = Table::new(&CString::new(TABLE_NAME).unwrap(), ProtoFamily::Inet);
    
    batch.add(&table, nftnl::MsgType::Add);

    let mut out_chain = Chain::new(&CString::new(OUT_CHAIN_NAME).unwrap(), &table);
    let mut in_chain = Chain::new(&CString::new(IN_CHAIN_NAME).unwrap(), &table);

    out_chain.set_hook(nftnl::Hook::Out, 0);
    in_chain.set_hook(nftnl::Hook::In, 0);

    out_chain.set_policy(nftnl::Policy::Accept);
    in_chain.set_policy(nftnl::Policy::Drop);

    batch.add(&out_chain, nftnl::MsgType::Add);
    batch.add(&in_chain, nftnl::MsgType::Add);

    let mut _rule = Rule::new(&in_chain);

    _rule.add_expr(&nft_expr!(meta nfproto));
    _rule.add_expr(&nft_expr!(cmp == libc::NFPROTO_IPV4 as u8));

    _rule.add_expr(&nft_expr!(meta l4proto));
    _rule.add_expr(&nft_expr!(cmp == libc::IPPROTO_TCP as u8));

    _rule.add_expr(&nftnl::expr::Payload::Transport(
        nftnl::expr::TransportHeaderField::Tcp(nftnl::expr::TcpHeaderField::Dport),
    ));
    _rule.add_expr(&nft_expr!(cmp == port.to_be()));

    _rule.add_expr(&nft_expr!(verdict accept));

    batch.add(&_rule, nftnl::MsgType::Add);

    let finalized_batch = batch.finalize();
    send_and_process(&finalized_batch)?;
    
    Ok(())
 
}