feat: router arp entry
This commit is contained in:
@ -1,29 +1,101 @@
|
||||
use crate::router::Router;
|
||||
use crate::router::{ArpEntry, Router};
|
||||
use anyhow::{bail, Result};
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
use log::debug;
|
||||
use reqwest::{Client, Method, Url};
|
||||
use rocket::async_trait;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::net::IpAddr;
|
||||
|
||||
pub struct MikrotikRouter {
|
||||
url: String,
|
||||
token: String,
|
||||
url: Url,
|
||||
username: String,
|
||||
password: String,
|
||||
client: Client,
|
||||
arp_interface: String,
|
||||
}
|
||||
|
||||
impl MikrotikRouter {
|
||||
pub fn new(url: &str, token: &str) -> Self {
|
||||
pub fn new(url: &str, username: &str, password: &str, arp_interface: &str) -> Self {
|
||||
Self {
|
||||
url: url.to_string(),
|
||||
token: token.to_string(),
|
||||
url: url.parse().unwrap(),
|
||||
username: username.to_string(),
|
||||
password: password.to_string(),
|
||||
client: Client::builder()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap(),
|
||||
arp_interface: arp_interface.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn req<T: DeserializeOwned, R: Serialize>(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
body: R,
|
||||
) -> Result<T> {
|
||||
let body = serde_json::to_string(&body)?;
|
||||
debug!(">> {} {}: {}", method.clone(), path, &body);
|
||||
let rsp = self
|
||||
.client
|
||||
.request(method.clone(), self.url.join(path)?)
|
||||
.header(
|
||||
"Authorization",
|
||||
format!(
|
||||
"Basic {}",
|
||||
STANDARD.encode(format!("{}:{}", self.username, self.password))
|
||||
),
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.body(body)
|
||||
.send()
|
||||
.await?;
|
||||
let status = rsp.status();
|
||||
let text = rsp.text().await?;
|
||||
#[cfg(debug_assertions)]
|
||||
debug!("<< {}", text);
|
||||
if status.is_success() {
|
||||
Ok(serde_json::from_str(&text)?)
|
||||
} else {
|
||||
bail!("{} {}: {}", method, path, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Router for MikrotikRouter {
|
||||
async fn add_arp_entry(
|
||||
&self,
|
||||
ip: IpAddr,
|
||||
mac: &[u8; 6],
|
||||
comment: Option<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
async fn list_arp_entry(&self) -> Result<Vec<ArpEntry>> {
|
||||
let rsp: Vec<ArpEntry> = self.req(Method::GET, "/rest/ip/arp", ()).await?;
|
||||
Ok(rsp)
|
||||
}
|
||||
|
||||
async fn add_arp_entry(&self, ip: IpAddr, mac: &str, comment: Option<&str>) -> Result<()> {
|
||||
let _rsp: ArpEntry = self
|
||||
.req(
|
||||
Method::PUT,
|
||||
"/rest/ip/arp",
|
||||
ArpEntry {
|
||||
address: ip.to_string(),
|
||||
mac_address: Some(mac.to_string()),
|
||||
interface: self.arp_interface.to_string(),
|
||||
comment: comment.map(|c| c.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_arp_entry(&self, id: &str) -> Result<()> {
|
||||
let _rsp: ArpEntry = self
|
||||
.req(Method::DELETE, &format!("/rest/ip/arp/{id}"), ())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use anyhow::Result;
|
||||
use rocket::async_trait;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use std::net::IpAddr;
|
||||
|
||||
/// Router defines a network device used to access the hosts
|
||||
@ -10,9 +11,27 @@ use std::net::IpAddr;
|
||||
///
|
||||
/// It also prevents people from re-assigning their IP to another in the range,
|
||||
#[async_trait]
|
||||
pub trait Router {
|
||||
async fn add_arp_entry(&self, ip: IpAddr, mac: &[u8; 6], comment: Option<&str>) -> Result<()>;
|
||||
pub trait Router: Send + Sync {
|
||||
async fn list_arp_entry(&self) -> Result<Vec<ArpEntry>>;
|
||||
async fn add_arp_entry(&self, ip: IpAddr, mac: &str, comment: Option<&str>) -> Result<()>;
|
||||
async fn remove_arp_entry(&self, id: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ArpEntry {
|
||||
#[serde(rename = ".id")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<String>,
|
||||
pub address: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(rename = "mac-address")]
|
||||
pub mac_address: Option<String>,
|
||||
pub interface: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "mikrotik")]
|
||||
mod mikrotik;
|
||||
#[cfg(feature = "mikrotik")]
|
||||
pub use mikrotik::*;
|
||||
|
Reference in New Issue
Block a user