1use ipnet::IpNet;
2use serde::{Deserialize, Serialize};
3use std::net::IpAddr;
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub enum Confidence {
11 Exact,
13 Approximate { note: String },
15 Manual { reason: String },
17 Unknown { raw: String },
19}
20
21#[derive(Debug, Clone, Default, Serialize, Deserialize)]
26pub struct NetworkConfig {
27 pub hostname: Option<String>,
28 pub domain_name: Option<String>,
29 pub interfaces: Vec<Interface>,
30 pub vlans: Vec<Vlan>,
31 pub routing: RoutingConfig,
32 pub acls: Vec<Acl>,
33 pub nat: Vec<NatRule>,
34 pub ntp: Vec<NtpServer>,
35 pub dns: Vec<IpAddr>,
36 pub snmp: Option<SnmpConfig>,
37 pub aaa: Option<AaaConfig>,
38 pub stp: Option<GlobalStp>,
39 pub logging: Option<LoggingConfig>,
40 pub users: Vec<LocalUser>,
41 pub line_vty: Option<LineVty>,
42 pub ssh: Option<SshConfig>,
43 pub banner: Option<String>,
44 pub unknown_blocks: Vec<UnknownBlock>,
46 pub platform_specific: Vec<UnknownBlock>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct GlobalStp {
56 pub mode: StpMode,
57 pub loopguard: bool,
58 pub portfast_default: bool,
59 pub bpduguard_default: bool,
60 pub vlan_priorities: Vec<StpVlanPriority>,
61}
62
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64pub enum StpMode {
65 RapidPvst,
66 Pvst,
67 Mst,
68 Rstp,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct StpVlanPriority {
73 pub vlans: Vec<u16>,
74 pub priority: u32,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct LoggingConfig {
83 pub buffered_size: Option<u32>,
84 pub console_level: Option<String>,
85 pub hosts: Vec<std::net::IpAddr>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct LocalUser {
94 pub name: String,
95 pub privilege: u8,
96 pub password_type: PasswordType,
97 pub password_hash: String,
98}
99
100#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub enum PasswordType {
102 Plaintext,
103 Type7, Md5, Scrypt, }
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct LineVty {
114 pub exec_timeout_min: u32,
115 pub exec_timeout_sec: u32,
116 pub transport_input: Vec<String>,
117 pub logging_synchronous: bool,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct SshConfig {
126 pub version: u8,
127 pub timeout: Option<u32>,
128 pub retries: Option<u8>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct Interface {
137 pub name: InterfaceName,
140 pub description: Option<String>,
141 pub addresses: Vec<IpAddress>,
142 pub shutdown: bool,
143 pub mtu: Option<u32>,
144 pub speed: Option<InterfaceSpeed>,
145 pub duplex: Option<Duplex>,
146 pub l2: Option<L2Config>,
147 pub helper_addresses: Vec<IpAddr>,
148 pub acl_in: Option<String>,
149 pub acl_out: Option<String>,
150 pub nat_direction: Option<NatDirection>,
151 pub hsrp: Vec<HsrpGroup>,
152 pub ospf: Option<InterfaceOspf>,
153 pub voice_vlan: Option<u16>,
154 pub storm_control: Option<StormControl>,
155 pub stp: InterfaceStp,
156 pub confidence: Confidence,
157}
158
159#[derive(Debug, Clone, Default, Serialize, Deserialize)]
160pub struct StormControl {
161 pub broadcast_level: Option<f32>,
162 pub multicast_level: Option<f32>,
163 pub unicast_level: Option<f32>,
164}
165
166#[derive(Debug, Clone, Default, Serialize, Deserialize)]
167pub struct InterfaceStp {
168 pub portfast: bool,
169 pub bpduguard: bool,
170 pub bpdufilter: bool,
171 pub guard_root: bool,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct InterfaceName {
176 pub kind: InterfaceKind,
177 pub id: String,
179 pub original: String,
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
184pub enum InterfaceKind {
185 GigabitEthernet,
186 FastEthernet,
187 TenGigabitEthernet,
188 Loopback,
189 Vlan,
190 Tunnel,
191 Serial,
192 BundleEther,
193 Management,
194 Unknown(String),
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct IpAddress {
199 pub prefix: IpNet,
200 pub secondary: bool,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub enum InterfaceSpeed {
205 Mbps(u32),
206 Auto,
207}
208
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
210pub enum Duplex {
211 Full,
212 Half,
213 Auto,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct L2Config {
218 pub mode: L2Mode,
219 pub access_vlan: Option<u16>,
220 pub trunk_allowed: Option<Vec<u16>>,
221 pub trunk_native: Option<u16>,
222}
223
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
225pub enum L2Mode {
226 Access,
227 Trunk,
228}
229
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231pub enum NatDirection {
232 Inside,
233 Outside,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct HsrpGroup {
242 pub group_id: u16,
243 pub virtual_ip: IpAddr,
244 pub priority: Option<u16>,
245 pub preempt: bool,
246 pub preempt_delay: Option<u32>,
247 pub timers: Option<HsrpTimers>,
248 pub track: Vec<HsrpTrack>,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct HsrpTimers {
253 pub hello_ms: u32,
254 pub hold_ms: u32,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct HsrpTrack {
259 pub object: u32,
260 pub decrement: u16,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct InterfaceOspf {
265 pub process_id: u32,
266 pub area: OspfArea,
267 pub cost: Option<u32>,
268 pub priority: Option<u8>,
269 pub timers: Option<OspfIfTimers>,
270 pub auth: Option<OspfAuth>,
271 pub passive: bool,
272 pub network_type: Option<OspfNetworkType>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct OspfIfTimers {
277 pub hello_interval: u32,
278 pub dead_interval: u32,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct Vlan {
287 pub id: u16,
288 pub name: Option<String>,
289 pub active: bool,
290}
291
292#[derive(Debug, Clone, Default, Serialize, Deserialize)]
297pub struct RoutingConfig {
298 pub static_routes: Vec<StaticRoute>,
299 pub ospf: Vec<OspfProcess>,
300 pub bgp: Option<BgpConfig>,
301 pub eigrp: Vec<EigrpProcess>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct StaticRoute {
306 pub prefix: IpNet,
307 pub next_hop: NextHop,
308 pub distance: Option<u8>,
309 pub tag: Option<u32>,
310 pub name: Option<String>,
311 pub permanent: bool,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
315pub enum NextHop {
316 Ip(IpAddr),
317 Interface(String),
318 IpAndInterface(IpAddr, String),
319 Null0,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct OspfProcess {
324 pub process_id: u32,
325 pub router_id: Option<IpAddr>,
326 pub areas: Vec<OspfAreaConfig>,
327 pub passive_interfaces: Vec<String>,
328 pub default_originate: Option<OspfDefaultOriginate>,
329 pub redistribute: Vec<OspfRedistribute>,
330 pub max_metric: bool,
331 pub auth: Option<OspfAuth>,
332 pub log_adjacency: bool,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct OspfAreaConfig {
337 pub area: OspfArea,
338 pub networks: Vec<OspfNetwork>,
339 pub area_type: OspfAreaType,
340 pub auth: Option<OspfAuth>,
341}
342
343#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
344pub enum OspfArea {
345 Backbone, Normal(u32),
347 IpFormat(IpAddr), }
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct OspfNetwork {
352 pub prefix: IpNet,
353 pub wildcard: bool, }
355
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357pub enum OspfAreaType {
358 Normal,
359 Stub,
360 StubNoSummary,
361 Nssa,
362 NssaNoSummary,
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub enum OspfAuth {
367 Simple(String),
368 Md5 { key_id: u8, key: String },
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
372pub enum OspfNetworkType {
373 Broadcast,
374 PointToPoint,
375 PointToMultipoint,
376 NonBroadcast,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
380pub struct OspfDefaultOriginate {
381 pub always: bool,
382 pub metric: Option<u32>,
383 pub metric_type: Option<u8>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct OspfRedistribute {
388 pub source: RedistributeSource,
389 pub metric: Option<u32>,
390 pub metric_type: Option<u8>,
391 pub subnets: bool,
392 pub tag: Option<u32>,
393 pub route_map: Option<String>,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub enum RedistributeSource {
398 Connected,
399 Static,
400 Bgp(u32),
401 Eigrp(u32),
402 Rip,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct BgpConfig {
407 pub asn: u32,
408 pub router_id: Option<IpAddr>,
409 pub neighbors: Vec<BgpNeighbor>,
410 pub peer_groups: Vec<BgpPeerGroup>,
411 pub networks: Vec<IpNet>,
413 pub address_families: Vec<BgpAddressFamily>,
415 pub redistribute: Vec<OspfRedistribute>,
416 pub log_neighbor_changes: bool,
417 pub bestpath: Option<String>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
421pub struct BgpNeighbor {
422 pub address: BgpNeighborAddr,
423 pub remote_as: u32,
424 pub description: Option<String>,
425 pub update_source: Option<String>,
426 pub next_hop_self: bool,
427 pub password: Option<String>,
428 pub shutdown: bool,
429 pub peer_group: Option<String>,
430 pub route_map_in: Option<String>,
431 pub route_map_out: Option<String>,
432 pub prefix_list_in: Option<String>,
433 pub prefix_list_out: Option<String>,
434 pub soft_reconfiguration: bool,
435 pub send_community: bool,
436 pub remove_private_as: bool,
437 pub default_originate: bool,
438 pub activate: bool,
439}
440
441#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
443pub enum BgpNeighborAddr {
444 Ip(IpAddr),
445 PeerGroup(String),
446}
447
448impl std::fmt::Display for BgpNeighborAddr {
449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
450 match self {
451 BgpNeighborAddr::Ip(ip) => write!(f, "{}", ip),
452 BgpNeighborAddr::PeerGroup(name) => write!(f, "{}", name),
453 }
454 }
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct BgpPeerGroup {
459 pub name: String,
460 pub remote_as: Option<u32>,
461 pub update_source: Option<String>,
462 pub next_hop_self: bool,
463 pub route_map_in: Option<String>,
464 pub route_map_out: Option<String>,
465 pub send_community: bool,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct BgpAddressFamily {
471 pub afi: BgpAfi,
472 pub safi: BgpSafi,
473 pub networks: Vec<IpNet>,
474 pub redistribute: Vec<OspfRedistribute>,
475 pub activated_neighbors: Vec<BgpNeighborAddr>,
477 pub deactivated_neighbors: Vec<BgpNeighborAddr>,
478 pub neighbor_settings: Vec<BgpNeighbor>,
479 pub default_information: bool,
480 pub aggregate_addresses: Vec<BgpAggregate>,
481}
482
483#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
484pub enum BgpAfi {
485 Ipv4,
486 Ipv6,
487 Vpnv4,
488 L2vpn,
489}
490
491#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
492pub enum BgpSafi {
493 Unicast,
494 Multicast,
495 Labeled,
496 Evpn,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct BgpAggregate {
501 pub prefix: IpNet,
502 pub summary_only: bool,
503 pub as_set: bool,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
507pub struct EigrpProcess {
508 pub asn: u32,
509 pub networks: Vec<OspfNetwork>,
510 pub passive_interfaces: Vec<String>,
511 pub redistribute: Vec<OspfRedistribute>,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
519pub struct Acl {
520 pub name: AclName,
521 pub acl_type: AclType,
522 pub entries: Vec<AclEntry>,
523}
524
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub enum AclName {
527 Named(String),
528 Numbered(u32),
529}
530
531#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
532pub enum AclType {
533 Standard, Extended, }
536
537#[derive(Debug, Clone, Serialize, Deserialize)]
538pub struct AclEntry {
539 pub sequence: Option<u32>,
540 pub action: AclAction,
541 pub protocol: Option<AclProtocol>,
542 pub src: AclMatch,
543 pub dst: Option<AclMatch>,
544 pub src_port: Option<AclPort>,
545 pub dst_port: Option<AclPort>,
546 pub established: bool,
547 pub log: bool,
548 pub remark: Option<String>,
549}
550
551#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
552pub enum AclAction {
553 Permit,
554 Deny,
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize)]
558pub enum AclProtocol {
559 Ip,
560 Tcp,
561 Udp,
562 Icmp,
563 Esp,
564 Ahp,
565 Number(u8),
566}
567
568#[derive(Debug, Clone, Serialize, Deserialize)]
569pub enum AclMatch {
570 Any,
571 Host(IpAddr),
572 Network { addr: IpAddr, wildcard: IpAddr },
573 Prefix(IpNet),
574}
575
576#[derive(Debug, Clone, Serialize, Deserialize)]
577pub enum AclPort {
578 Eq(u16),
579 Ne(u16),
580 Lt(u16),
581 Gt(u16),
582 Range(u16, u16),
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct NatRule {
591 pub rule_type: NatType,
592 pub acl: Option<String>,
593 pub pool: Option<NatPool>,
594 pub interface_overload: bool,
595 pub static_entry: Option<NatStaticEntry>,
596}
597
598#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
599pub enum NatType {
600 Dynamic, Overload, Static, }
604
605#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct NatPool {
607 pub name: String,
608 pub start: IpAddr,
609 pub end: IpAddr,
610 pub prefix: Option<IpNet>,
611 pub overload: bool,
612}
613
614#[derive(Debug, Clone, Serialize, Deserialize)]
615pub struct NatStaticEntry {
616 pub local: IpAddr,
617 pub global: IpAddr,
618 pub local_port: Option<u16>,
619 pub global_port: Option<u16>,
620 pub protocol: Option<AclProtocol>,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
628pub struct NtpServer {
629 pub address: IpAddr,
630 pub prefer: bool,
631 pub key: Option<u32>,
632 pub source_interface: Option<String>,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct SnmpConfig {
637 pub communities: Vec<SnmpCommunity>,
638 pub location: Option<String>,
639 pub contact: Option<String>,
640 pub traps: Vec<String>,
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct SnmpCommunity {
645 pub name: String,
646 pub access: SnmpAccess,
647 pub acl: Option<String>,
648}
649
650#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
651pub enum SnmpAccess {
652 Ro,
653 Rw,
654}
655
656#[derive(Debug, Clone, Serialize, Deserialize)]
657pub struct AaaConfig {
658 pub new_model: bool,
659 pub authentication: Vec<AaaMethod>,
660 pub authorization: Vec<AaaMethod>,
661}
662
663#[derive(Debug, Clone, Serialize, Deserialize)]
664pub struct AaaMethod {
665 pub list_name: String,
666 pub methods: Vec<String>,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
674pub struct UnknownBlock {
675 pub line: usize,
677 pub context: String,
679 pub raw: String,
681}
682
683pub type ProcessId = u32;
688
689impl Default for Interface {
690 fn default() -> Self {
691 Interface {
692 name: InterfaceName {
693 kind: InterfaceKind::Unknown(String::new()),
694 id: String::new(),
695 original: String::new(),
696 },
697 description: None,
698 addresses: vec![],
699 shutdown: false,
700 mtu: None,
701 speed: None,
702 duplex: None,
703 l2: None,
704 helper_addresses: vec![],
705 acl_in: None,
706 acl_out: None,
707 nat_direction: None,
708 hsrp: vec![],
709 ospf: None,
710 voice_vlan: None,
711 storm_control: None,
712 stp: InterfaceStp::default(),
713 confidence: Confidence::Exact,
714 }
715 }
716}
717
718impl InterfaceName {
719 pub fn parse(raw: &str) -> Self {
720 let original = raw.to_string();
721
722 let expanded = expand_interface_name(raw);
724
725 let (kind_str, id) = split_interface_name(&expanded);
727
728 let kind = match kind_str.to_lowercase().as_str() {
729 "gigabitethernet" => InterfaceKind::GigabitEthernet,
730 "fastethernet" => InterfaceKind::FastEthernet,
731 "tengigabitethernet" | "tengige" => InterfaceKind::TenGigabitEthernet,
732 "loopback" => InterfaceKind::Loopback,
733 "vlan" => InterfaceKind::Vlan,
734 "tunnel" => InterfaceKind::Tunnel,
735 "serial" => InterfaceKind::Serial,
736 "bundle-ether" | "bundleether" => InterfaceKind::BundleEther,
737 "management" => InterfaceKind::Management,
738 other => InterfaceKind::Unknown(other.to_string()),
739 };
740
741 InterfaceName { kind, id, original }
742 }
743}
744
745fn expand_interface_name(raw: &str) -> String {
746 let prefixes = [
748 ("Gi", "GigabitEthernet"),
749 ("Fa", "FastEthernet"),
750 ("Te", "TenGigabitEthernet"),
751 ("Lo", "Loopback"),
752 ("Tu", "Tunnel"),
753 ("Se", "Serial"),
754 ("Vl", "Vlan"),
755 ("Mg", "Management"),
756 ];
757
758 for (short, full) in &prefixes {
759 if raw.starts_with(short) && !raw.to_lowercase().starts_with(&full.to_lowercase()) {
760 return format!("{}{}", full, &raw[short.len()..]);
761 }
762 }
763 raw.to_string()
764}
765
766fn split_interface_name(name: &str) -> (&str, String) {
767 let pos = name.find(|c: char| c.is_ascii_digit());
769 match pos {
770 Some(i) => (&name[..i], name[i..].to_string()),
771 None => (name, String::new()),
772 }
773}
774
775impl OspfArea {
776 pub fn parse(s: &str) -> Self {
777 if let Ok(n) = s.parse::<u32>() {
778 if n == 0 {
779 OspfArea::Backbone
780 } else {
781 OspfArea::Normal(n)
782 }
783 } else if let Ok(ip) = s.parse::<IpAddr>() {
784 if ip == "0.0.0.0".parse::<IpAddr>().unwrap() {
785 OspfArea::Backbone
786 } else {
787 OspfArea::IpFormat(ip)
788 }
789 } else {
790 OspfArea::Normal(0) }
792 }
793}