qemu_command_builder/
netdev.rs

1use std::net::{Ipv4Addr, Ipv6Addr};
2use std::path::PathBuf;
3
4use crate::chardev::CharDev;
5use crate::common::OnOff;
6use crate::to_command::ToArg;
7use crate::to_command::ToCommand;
8use bon::Builder;
9use ipnet::{Ipv4Net, Ipv6Net};
10
11#[derive(Builder)]
12pub struct SMB {
13    dir: PathBuf,
14    smbserver: Option<String>,
15}
16
17#[derive(Default)]
18pub enum TcpUdp {
19    #[default]
20    Tcp,
21    Udp,
22}
23
24pub enum ScriptOrNot {
25    Script(PathBuf),
26    None,
27}
28
29impl ToCommand for ScriptOrNot {
30    fn to_command(&self) -> Vec<String> {
31        match self {
32            ScriptOrNot::Script(path) => {
33                vec![path.display().to_string()]
34            }
35            ScriptOrNot::None => {
36                vec!["no".to_string()]
37            }
38        }
39    }
40}
41#[derive(Builder)]
42pub struct HostForward {
43    protocol: Option<TcpUdp>,
44    hostaddr: Option<String>,
45    hostport: u16,
46    guestaddr: Option<String>,
47    guestport: u16,
48}
49
50pub enum GuestForwardTarget {
51    Device(CharDev),
52    Cmd((String, Vec<String>)),
53}
54#[derive(Builder)]
55pub struct GuestForward {
56    server: String,
57    port: u16,
58    target: GuestForwardTarget,
59}
60#[derive(Builder)]
61pub struct User {
62    id: String,
63    ipv4: Option<OnOff>,
64    net: Option<Ipv4Net>,
65    host: Option<Ipv4Addr>,
66    ipv6: Option<OnOff>,
67    ipv6_net: Option<Ipv6Net>,
68    ipv6_host: Option<Ipv6Addr>,
69    restrict: Option<OnOff>,
70    hostname: Option<String>,
71    dhcpstart: Option<Ipv4Addr>,
72    dns: Option<Ipv4Addr>,
73    ipv6_dns: Option<Ipv6Addr>,
74    dnssearch: Option<Vec<String>>,
75    domainname: Option<String>,
76    tftp: Option<PathBuf>,
77    tftp_server_name: Option<String>,
78    bootfile: Option<PathBuf>,
79    smb: Option<SMB>,
80    hostfwd: Option<Vec<HostForward>>,
81    guestfwd: Option<Vec<GuestForward>>,
82}
83
84impl ToCommand for User {
85    fn to_command(&self) -> Vec<String> {
86        let mut args = vec!["user".to_string(), format!("id={}", self.id.to_string())];
87
88        if let Some(ipv4) = &self.ipv4 {
89            args.push(format!("ipv4={}", ipv4.to_arg()));
90        }
91        if let Some(net) = &self.net {
92            args.push(format!("net={}", net));
93        }
94        if let Some(host) = &self.host {
95            args.push(format!("host={}", host));
96        }
97        if let Some(ipv6) = &self.ipv6 {
98            args.push(format!("ipv6={}", ipv6.to_arg()));
99        }
100        if let Some(ipv6_net) = &self.ipv6_net {
101            args.push(format!("ipv6-net={}", ipv6_net));
102        }
103        if let Some(ipv6_host) = &self.ipv6_host {
104            args.push(format!("ipv6-host={}", ipv6_host));
105        }
106        if let Some(restrict) = &self.restrict {
107            args.push(format!("restrict={}", restrict.to_arg()));
108        }
109        if let Some(hostname) = &self.hostname {
110            args.push(format!("hostname={}", hostname));
111        }
112        if let Some(dhcpstart) = &self.dhcpstart {
113            args.push(format!("dhcpstart={}", dhcpstart));
114        }
115        if let Some(dns) = &self.dns {
116            args.push(format!("dns={}", dns));
117        }
118        if let Some(ipv6_dns) = &self.ipv6_dns {
119            args.push(format!("ipv6-dns={}", ipv6_dns));
120        }
121        if let Some(dnssearch) = &self.dnssearch {
122            args.push(format!("dnssearch={}", dnssearch.join(",")));
123        }
124        if let Some(domainname) = &self.domainname {
125            args.push(format!("domainname={}", domainname));
126        }
127        if let Some(tftp) = &self.tftp {
128            args.push(format!("tftp={}", tftp.display()));
129        }
130        if let Some(tftp_server_name) = &self.tftp_server_name {
131            args.push(format!("tftp-server-name={}", tftp_server_name));
132        }
133        if let Some(bootfile) = &self.bootfile {
134            args.push(format!("bootfile={}", bootfile.display()));
135        }
136        if let Some(smb) = &self.smb {
137            args.push(format!("smb={}", smb.dir.display()));
138            if let Some(smbserver) = &smb.smbserver {
139                args.push(format!("smbserver={}", smbserver));
140            }
141        }
142        if let Some(hostfwds) = &self.hostfwd {
143            for hostfwd in hostfwds {
144                let mut subargs = vec![];
145                if let Some(proto) = &hostfwd.protocol {
146                    match proto {
147                        TcpUdp::Tcp => {
148                            subargs.push("tcp".to_string());
149                        }
150                        TcpUdp::Udp => {
151                            subargs.push("udp".to_string());
152                        }
153                    }
154                }
155                if let Some(hostaddr) = &hostfwd.hostaddr {
156                    subargs.push(hostaddr.to_string());
157                }
158                subargs.push(format!("{}-", hostfwd.hostport));
159                if let Some(guestaddr) = &hostfwd.guestaddr {
160                    subargs.push(guestaddr.to_string());
161                }
162                subargs.push(format!("{}", hostfwd.guestport));
163                args.push(subargs.join(";"));
164            }
165        }
166        if let Some(guestfwds) = &self.guestfwd {
167            for guestfwd in guestfwds {
168                let mut subargs = vec!["tcp".to_string()];
169
170                subargs.push(guestfwd.server.to_string());
171                subargs.push(format!("{}", guestfwd.port));
172
173                match &guestfwd.target {
174                    GuestForwardTarget::Device(dev) => {
175                        subargs.push(format!("device={}", dev.to_command().join(" ")));
176                    }
177                    GuestForwardTarget::Cmd((cmd, args)) => {
178                        subargs.push(format!("cmd:{} {}", cmd, args.join(" ")));
179                    }
180                }
181                args.push(subargs.join(":"));
182            }
183        }
184        vec![args.join(",")]
185    }
186}
187#[derive(Builder)]
188pub struct Tap {
189    id: String,
190    fd: Option<String>,
191    fds: Option<Vec<String>>,
192    ifname: Option<String>,
193    script: Option<ScriptOrNot>,
194    downscript: Option<ScriptOrNot>,
195    br: Option<String>,
196    helper: Option<String>,
197    sndbuf: Option<usize>,
198    vnet_hdr: Option<OnOff>,
199    vhost: Option<OnOff>,
200    vhostfd: Option<String>,
201    vhostforce: Option<OnOff>,
202    queues: Option<usize>,
203    poll_us: Option<usize>,
204}
205
206impl ToCommand for Tap {
207    fn to_command(&self) -> Vec<String> {
208        let mut args = vec!["tap".to_string(), format!("id={}", self.id.to_string())];
209
210        if let Some(fd) = &self.fd {
211            args.push(format!("fd={}", fd));
212        }
213        if let Some(fds) = &self.fds {
214            args.push(format!("fds={}", fds.join(":")));
215        }
216        if let Some(ifname) = &self.ifname {
217            args.push(format!("ifname={}", ifname));
218        }
219        if let Some(script) = &self.script {
220            args.push(format!("script={}", script.to_command().join("")));
221        }
222        if let Some(downscript) = &self.downscript {
223            args.push(format!("downscript={}", downscript.to_command().join("")));
224        }
225        if let Some(br) = &self.br {
226            args.push(format!("br={}", br));
227        }
228        if let Some(helper) = &self.helper {
229            args.push(format!("helper={}", helper));
230        }
231        if let Some(sndbuf) = self.sndbuf {
232            args.push(format!("sndbuf={}", sndbuf));
233        }
234        if let Some(vnet_hdr) = &self.vnet_hdr {
235            args.push(format!("vnet_hdr={}", vnet_hdr.to_arg()));
236        }
237        if let Some(vhost) = &self.vhost {
238            args.push(format!("vhost={}", vhost.to_arg()));
239        }
240        if let Some(vhostfd) = &self.vhostfd {
241            args.push(format!("vhostfd={}", vhostfd));
242        }
243        if let Some(vhostforce) = &self.vhostforce {
244            args.push(format!("vhostforce={}", vhostforce.to_arg()));
245        }
246        if let Some(queues) = self.queues {
247            args.push(format!("queues={}", queues));
248        }
249        if let Some(poll_us) = self.poll_us {
250            args.push(format!("poll_us={}", poll_us));
251        }
252
253        args
254    }
255}
256#[derive(Builder)]
257pub struct Bridge {
258    id: String,
259    bridge: Option<String>,
260    helper: Option<String>,
261}
262
263impl ToCommand for Bridge {
264    fn to_command(&self) -> Vec<String> {
265        let mut cmd = vec!["bridge".to_string(), format!("id={}", self.id)];
266
267        if let Some(br) = &self.bridge {
268            cmd.push(format!("bridge={}", br));
269        }
270        if let Some(helper) = &self.helper {
271            cmd.push(format!("helper={}", helper));
272        }
273        cmd
274    }
275}
276
277#[derive(Builder)]
278pub struct HostAndPort {
279    host: String,
280    port: u16,
281}
282#[derive(Builder)]
283pub struct HostAndMaybePort {
284    host: String,
285    port: Option<u16>,
286}
287#[derive(Builder)]
288pub struct SocketRegular {
289    id: String,
290    fd: Option<String>,
291    listen: Option<HostAndMaybePort>,
292    connection: Option<HostAndPort>,
293}
294
295impl ToCommand for SocketRegular {
296    fn to_command(&self) -> Vec<String> {
297        let mut cmd = vec!["socket".to_string(), format!("id={}", self.id)];
298
299        if let Some(fd) = &self.fd {
300            cmd.push(format!("fd={}", fd));
301        }
302        if let Some(listen) = &self.listen {
303            if let Some(port) = &listen.port {
304                cmd.push(format!("listen={}:{}", listen.host, port));
305            } else {
306                cmd.push(format!("listen={}", listen.host));
307            }
308            if let Some(connection) = &self.connection {
309                cmd.push(format!("{}:{}", connection.host, connection.port));
310            }
311        }
312
313        vec![cmd.join(",")]
314    }
315}
316#[derive(Builder)]
317pub struct SocketMulticast {
318    id: String,
319    fd: Option<String>,
320    mcast: Option<HostAndPort>,
321    localaddr: Option<String>,
322}
323
324impl ToCommand for SocketMulticast {
325    fn to_command(&self) -> Vec<String> {
326        let mut cmd = vec!["socket".to_string(), format!("id={}", self.id)];
327
328        if let Some(fd) = &self.fd {
329            cmd.push(format!("fd={}", fd));
330        }
331        if let Some(mcast) = &self.mcast {
332            cmd.push(format!("mcast={}:{}", mcast.host, mcast.port));
333        }
334        if let Some(localaddr) = &self.localaddr {
335            cmd.push(format!("localaddr={}", localaddr));
336        }
337        vec![cmd.join(",")]
338    }
339}
340#[derive(Builder)]
341pub struct SocketUdpTunnel {
342    id: String,
343    fd: Option<String>,
344    udp: Option<HostAndPort>,
345    localaddr: Option<HostAndPort>,
346}
347
348impl ToCommand for SocketUdpTunnel {
349    fn to_command(&self) -> Vec<String> {
350        let mut cmd = vec!["socket".to_string(), format!("id={}", self.id)];
351
352        if let Some(fd) = &self.fd {
353            cmd.push(format!("fd={}", fd));
354        }
355        if let Some(udp) = &self.udp {
356            cmd.push(format!("udp={}:{}", udp.host, udp.port));
357        }
358        if let Some(localaddr) = &self.localaddr {
359            cmd.push(format!("localaddr={}:{}", localaddr.host, localaddr.port));
360        }
361        vec![cmd.join(",")]
362    }
363}
364pub enum Socket {
365    SocketRegular(SocketRegular),
366    Multicast(SocketMulticast),
367    UDPTunnel(SocketUdpTunnel),
368}
369
370impl ToCommand for Socket {
371    fn to_command(&self) -> Vec<String> {
372        match self {
373            Socket::SocketRegular(s) => s.to_command(),
374            Socket::Multicast(s) => s.to_command(),
375            Socket::UDPTunnel(s) => s.to_command(),
376        }
377    }
378}
379#[derive(Builder)]
380pub struct StreamOverTcp {
381    id: String,
382    server: Option<OnOff>,
383    addr_host: String,
384    addr_port: u16,
385    to: Option<u16>,
386    numeric: Option<OnOff>,
387    keep_alive: Option<OnOff>,
388    mptcp: Option<OnOff>,
389    addr_ipv4: Option<OnOff>,
390    addr_ipv6: Option<OnOff>,
391    reconnect_ms: Option<usize>,
392}
393
394impl ToCommand for StreamOverTcp {
395    fn to_command(&self) -> Vec<String> {
396        let mut cmd = vec!["stream".to_string(), format!("id={}", self.id)];
397
398        if let Some(server) = &self.server {
399            cmd.push(format!("server={}", server.to_arg()));
400        }
401        cmd.push("add.type=inet".to_string());
402        cmd.push(format!("addr.host={}", self.addr_host));
403        cmd.push(format!("addr.port={}", self.addr_port));
404        if let Some(to) = &self.to {
405            cmd.push(format!("to={}", to));
406        }
407        if let Some(numeric) = &self.numeric {
408            cmd.push(format!("numeric={}", numeric.to_arg()));
409        }
410        if let Some(keep_alive) = &self.keep_alive {
411            cmd.push(format!("keep-alive={}", keep_alive.to_arg()));
412        }
413        if let Some(mptcp) = &self.mptcp {
414            cmd.push(format!("mptcp={}", mptcp.to_arg()));
415        }
416        if let Some(ipv4) = &self.addr_ipv4 {
417            cmd.push(format!("addr.ipv4={}", ipv4.to_arg()));
418        }
419        if let Some(ipv6) = &self.addr_ipv6 {
420            cmd.push(format!("addr.ipv6={}", ipv6.to_arg()));
421        }
422        if let Some(reconnect_ms) = self.reconnect_ms {
423            cmd.push(format!("reconnect-ms={}", reconnect_ms));
424        }
425        vec![cmd.join(",")]
426    }
427}
428#[derive(Builder)]
429pub struct StreamOverUds {
430    id: String,
431    server: Option<OnOff>,
432    addr_path: String,
433    abstract_arg: Option<OnOff>,
434    tight: Option<OnOff>,
435    reconnect_ms: Option<usize>,
436}
437
438impl ToCommand for StreamOverUds {
439    fn to_command(&self) -> Vec<String> {
440        let mut cmd = vec!["stream".to_string(), format!("id={}", self.id)];
441
442        if let Some(server) = &self.server {
443            cmd.push(format!("server={}", server.to_arg()));
444        }
445        cmd.push("add.type=unix".to_string());
446        cmd.push(format!("addr.path={}", self.addr_path));
447        if let Some(abstract_arg) = &self.abstract_arg {
448            cmd.push(format!("abstract={}", abstract_arg.to_arg()));
449        }
450        if let Some(tight) = &self.tight {
451            cmd.push(format!("tight={}", tight.to_arg()));
452        }
453        if let Some(reconnect_ms) = self.reconnect_ms {
454            cmd.push(format!("reconnect-ms={}", reconnect_ms));
455        }
456        vec![cmd.join(",")]
457    }
458}
459#[derive(Builder)]
460pub struct StreamOverFd {
461    id: String,
462    server: Option<OnOff>,
463    addr_str: String,
464    reconnect_ms: Option<usize>,
465}
466
467impl ToCommand for StreamOverFd {
468    fn to_command(&self) -> Vec<String> {
469        let mut cmd = vec!["stream".to_string(), format!("id={}", self.id)];
470
471        if let Some(server) = &self.server {
472            cmd.push(format!("server={}", server.to_arg()));
473        }
474        cmd.push("add.type=fd".to_string());
475        cmd.push(format!("addr.str={}", self.addr_str));
476        if let Some(reconnect_ms) = self.reconnect_ms {
477            cmd.push(format!("reconnect-ms={}", reconnect_ms));
478        }
479        vec![cmd.join(",")]
480    }
481}
482pub enum Stream {
483    StreamOverTcp(StreamOverTcp),
484    StreamOverUds(StreamOverUds),
485    StreamOverFd(StreamOverFd),
486}
487
488impl ToCommand for Stream {
489    fn to_command(&self) -> Vec<String> {
490        match self {
491            Stream::StreamOverTcp(s) => s.to_command(),
492            Stream::StreamOverUds(s) => s.to_command(),
493            Stream::StreamOverFd(s) => s.to_command(),
494        }
495    }
496}
497#[derive(Builder)]
498pub struct DgramMulticast {
499    id: String,
500    remote_host: String,
501    remote_port: u16,
502    local_host: Option<String>,
503}
504
505impl ToCommand for DgramMulticast {
506    fn to_command(&self) -> Vec<String> {
507        let mut args = vec![
508            "dgram".to_string(),
509            format!("id={}", self.id.to_string()),
510            "remote.type=inet".to_string(),
511            format!("remote.host={}", self.remote_host),
512            format!("remote.port={}", self.remote_port),
513        ];
514
515        if let Some(local_host) = &self.local_host {
516            args.push("local.type=inet".to_string());
517            args.push(format!("local.host={}", local_host));
518        }
519        args
520    }
521}
522#[derive(Builder)]
523pub struct DgramMulticastUdpFd {
524    id: String,
525    remote_host: String,
526    remote_port: u16,
527    local_str: Option<String>,
528}
529
530impl ToCommand for DgramMulticastUdpFd {
531    fn to_command(&self) -> Vec<String> {
532        let mut args = vec![
533            "dgram".to_string(),
534            format!("id={}", self.id.to_string()),
535            "remote.type=inet".to_string(),
536            format!("remote.host={}", self.remote_host),
537            format!("remote.port={}", self.remote_port),
538        ];
539
540        if let Some(local_str) = &self.local_str {
541            args.push("local.type=fd".to_string());
542            args.push(format!("local.str={}", local_str));
543        }
544        args
545    }
546}
547#[derive(Builder)]
548pub struct DgramSocket {
549    id: String,
550    local_host: String,
551    local_port: usize,
552    remote_host: Option<String>,
553    remote_port: Option<u16>,
554}
555
556impl ToCommand for DgramSocket {
557    fn to_command(&self) -> Vec<String> {
558        let mut args = vec![
559            "dgram".to_string(),
560            format!("id={}", self.id.to_string()),
561            "local.type=inet".to_string(),
562            format!("local.host={}", self.local_host),
563            format!("local.port={}", self.local_port),
564        ];
565
566        if let Some(remote_host) = &self.remote_host {
567            args.push("remote.type=inet".to_string());
568            args.push(format!("remote.host={}", remote_host));
569        }
570        if let Some(remote_port) = &self.remote_port {
571            args.push(format!("remote.port={}", remote_port));
572        }
573        args
574    }
575}
576
577#[derive(Builder)]
578pub struct DgramUds {
579    id: String,
580    local_path: PathBuf,
581    remote_path: Option<PathBuf>,
582}
583
584impl ToCommand for DgramUds {
585    fn to_command(&self) -> Vec<String> {
586        let mut args = vec![
587            "dgram".to_string(),
588            format!("id={}", self.id.to_string()),
589            "local.type=unix".to_string(),
590            format!("local.path={}", self.local_path.display()),
591        ];
592        if let Some(remote) = &self.remote_path {
593            args.push("remote.type=unix".to_string());
594            args.push(format!("remote.path={}", remote.display()));
595        }
596        args
597    }
598}
599#[derive(Builder)]
600pub struct DgramFd {
601    id: String,
602    local_str: String,
603}
604
605impl ToCommand for DgramFd {
606    fn to_command(&self) -> Vec<String> {
607        vec![
608            "dgram".to_string(),
609            format!("id={}", self.id.to_string()),
610            "local.type=fd".to_string(),
611            format!("local.str={}", self.local_str),
612        ]
613    }
614}
615pub enum Dgram {
616    DgramMulticast(DgramMulticast),
617    DgramMulticastUdpFd(DgramMulticastUdpFd),
618    DgramSocket(DgramSocket),
619    DgramUds(DgramUds),
620    DgramFd(DgramFd),
621}
622
623impl ToCommand for Dgram {
624    fn to_command(&self) -> Vec<String> {
625        match self {
626            Dgram::DgramMulticast(args) => args.to_command(),
627            Dgram::DgramMulticastUdpFd(args) => args.to_command(),
628            Dgram::DgramSocket(args) => args.to_command(),
629            Dgram::DgramUds(args) => args.to_command(),
630            Dgram::DgramFd(args) => args.to_command(),
631        }
632    }
633}
634#[derive(Builder)]
635pub struct Vde {
636    id: String,
637    sock: Option<PathBuf>,
638    port: Option<u16>,
639    group: Option<String>,
640    mode: Option<String>,
641}
642
643impl ToCommand for Vde {
644    fn to_command(&self) -> Vec<String> {
645        let mut args = vec!["vde".to_string(), format!("id={}", self.id.to_string())];
646
647        if let Some(sock) = &self.sock {
648            args.push(format!("sock={}", sock.display()));
649        }
650        if let Some(port) = self.port {
651            args.push(format!("port={}", port));
652        }
653        if let Some(group) = &self.group {
654            args.push(format!("group={}", group));
655        }
656        if let Some(mode) = &self.mode {
657            args.push(format!("mode={}", mode));
658        }
659        args
660    }
661}
662#[derive(Builder)]
663pub struct NetMap {
664    id: String,
665    ifname: String,
666    devname: Option<String>,
667}
668
669impl ToCommand for NetMap {
670    fn to_command(&self) -> Vec<String> {
671        let mut args = vec!["netmap".to_string(), format!("id={}", self.id.to_string())];
672        args.push(format!("ifname={}", self.ifname));
673        if let Some(devname) = &self.devname {
674            args.push(format!("devname={}", devname));
675        }
676        args
677    }
678}
679pub enum NativeSkb {
680    Native,
681    Skb,
682}
683
684impl ToArg for NativeSkb {
685    fn to_arg(&self) -> &str {
686        match self {
687            NativeSkb::Native => "native",
688            NativeSkb::Skb => "skb",
689        }
690    }
691}
692#[derive(Builder)]
693pub struct AfXdp {
694    id: String,
695    ifname: String,
696    mode: Option<NativeSkb>,
697    force_copy: Option<OnOff>,
698    queues: Option<usize>,
699    start_queue: Option<usize>,
700    inhibit: Option<OnOff>,
701    sock_fds: Option<Vec<String>>,
702}
703
704impl ToCommand for AfXdp {
705    fn to_command(&self) -> Vec<String> {
706        let mut args = vec![
707            "af-xdp".to_string(),
708            format!("id={}", self.id.to_string()),
709            format!("ifname={}", self.ifname),
710        ];
711
712        if let Some(mode) = &self.mode {
713            args.push(format!("mode={}", mode.to_arg()));
714        }
715        if let Some(force_copy) = &self.force_copy {
716            args.push(format!("force-copy={}", force_copy.to_arg()));
717        }
718        if let Some(queues) = self.queues {
719            args.push(format!("queues={}", queues));
720        }
721        if let Some(start_queue) = self.start_queue {
722            args.push(format!("start-queue={}", start_queue));
723        }
724        if let Some(inhibit) = &self.inhibit {
725            args.push(format!("inhibit={}", inhibit.to_arg()));
726        }
727        if let Some(sock_fds) = &self.sock_fds {
728            args.push(format!("sock-fds={}", sock_fds.join(":")));
729        }
730        args
731    }
732}
733#[derive(Builder)]
734pub struct VhostUser {
735    id: String,
736    chardev: String,
737    vhostforce: Option<OnOff>,
738}
739
740impl ToCommand for VhostUser {
741    fn to_command(&self) -> Vec<String> {
742        let mut args = vec![
743            "vhost-user".to_string(),
744            format!("id={}", self.id.to_string()),
745            format!("chardev={}", self.chardev),
746        ];
747
748        if let Some(vhostforce) = &self.vhostforce {
749            args.push(format!("vhostforce={}", vhostforce.to_arg()));
750        }
751        args
752    }
753}
754#[derive(Builder)]
755pub struct VhostVdpa {
756    id: String,
757    vhostdev: Option<PathBuf>,
758    vhostfd: Option<String>,
759}
760
761impl ToCommand for VhostVdpa {
762    fn to_command(&self) -> Vec<String> {
763        let mut args = vec![
764            "vhost-vdpa".to_string(),
765            format!("id={}", self.id.to_string()),
766        ];
767        if let Some(vhostdev) = &self.vhostdev {
768            args.push(format!("vhostdev={}", vhostdev.to_str().unwrap()));
769        }
770        if let Some(vhostfd) = &self.vhostfd {
771            args.push(format!("vhostfd={}", vhostfd));
772        }
773        args
774    }
775}
776#[derive(Builder)]
777pub struct VmnetHost {
778    id: String,
779    isolated: Option<OnOff>,
780    net_uuid: Option<String>,
781    start_address: Option<String>,
782    end_address: Option<String>,
783    subnet_mask: Option<String>,
784}
785
786impl ToCommand for VmnetHost {
787    fn to_command(&self) -> Vec<String> {
788        let mut args = vec![
789            "vmnet-host".to_string(),
790            format!("id={}", self.id.to_string()),
791        ];
792
793        if let Some(isolated) = &self.isolated {
794            args.push(format!("isolated={}", isolated.to_arg()));
795        }
796        if let Some(net_uuid) = &self.net_uuid {
797            args.push(format!("net_uuid={}", net_uuid));
798        }
799        if let Some(start_address) = &self.start_address {
800            args.push(format!("start-address={}", start_address));
801        }
802        if let Some(end_address) = &self.end_address {
803            args.push(format!("end-address={}", end_address));
804        }
805        if let Some(subnet_mask) = &self.subnet_mask {
806            args.push(format!("subnet-mask={}", subnet_mask));
807        }
808        args
809    }
810}
811
812#[derive(Builder)]
813pub struct VmnetShared {
814    id: String,
815    isolated: Option<OnOff>,
816    nat66_prefix: Option<String>,
817    start_address: Option<String>,
818    end_address: Option<String>,
819    subnet_mask: Option<String>,
820}
821
822impl ToCommand for VmnetShared {
823    fn to_command(&self) -> Vec<String> {
824        let mut args = vec![
825            "vmnet-shared".to_string(),
826            format!("id={}", self.id.to_string()),
827        ];
828
829        if let Some(isolated) = &self.isolated {
830            args.push(format!("isolated={}", isolated.to_arg()));
831        }
832        if let Some(nat66_prefix) = &self.nat66_prefix {
833            args.push(format!("nat66-prefix={}", nat66_prefix));
834        }
835        if let Some(start_address) = &self.start_address {
836            args.push(format!("start-address={}", start_address));
837        }
838        if let Some(end_address) = &self.end_address {
839            args.push(format!("end-address={}", end_address));
840        }
841        if let Some(subnet_mask) = &self.subnet_mask {
842            args.push(format!("subnet-mask={}", subnet_mask));
843        }
844        args
845    }
846}
847#[derive(Builder)]
848pub struct VmnetBridged {
849    id: String,
850    ifname: String,
851    isolated: Option<OnOff>,
852}
853
854impl ToCommand for VmnetBridged {
855    fn to_command(&self) -> Vec<String> {
856        let mut args = vec![
857            "vmnet-bridged".to_string(),
858            format!("id={}", self.id.to_string()),
859            format!("ifname={}", self.ifname),
860        ];
861
862        if let Some(isolated) = &self.isolated {
863            args.push(format!("isolated={}", isolated.to_arg()));
864        }
865        args
866    }
867}
868#[derive(Builder)]
869pub struct Hubport {
870    id: String,
871    hubid: usize,
872    netdev: Option<String>,
873}
874
875impl ToCommand for Hubport {
876    fn to_command(&self) -> Vec<String> {
877        let mut args = vec![
878            "hubport".to_string(),
879            format!("id={}", self.id.to_string()),
880            format!("hubid={}", self.hubid),
881        ];
882
883        if let Some(netdev) = &self.netdev {
884            args.push(format!("netdev={}", netdev));
885        }
886        args
887    }
888}
889pub enum NetDev {
890    User(User),
891    // TODO L2tpv3,
892    Tap(Tap),
893    Bridge(Bridge),
894    Socket(Socket),
895    Stream(Stream),
896    Dgram(Dgram),
897    Vde(Vde),
898    Netmap(NetMap),
899    AfXdp(AfXdp),
900    VhostUser(VhostUser),
901    VhostVdpa(VhostVdpa),
902    VmnetHost(VmnetHost),
903    VmnetShared(VmnetShared),
904    VmnetBridged(VmnetBridged),
905    Hubport(Hubport),
906}
907
908impl ToCommand for NetDev {
909    fn to_command(&self) -> Vec<String> {
910        let mut cmd = vec![];
911
912        cmd.push("-netdev".to_string());
913
914        let mut args: Vec<String> = vec![];
915
916        match self {
917            NetDev::User(user) => {
918                args.append(&mut user.to_command());
919            }
920            //NetDev::L2tpv3 => {}
921            NetDev::Tap(tap) => {
922                args.append(&mut tap.to_command());
923            }
924            NetDev::Bridge(bridge) => {
925                args.append(&mut bridge.to_command());
926            }
927            NetDev::Socket(socket) => {
928                args.append(&mut socket.to_command());
929            }
930            NetDev::Stream(stream) => {
931                args.append(&mut stream.to_command());
932            }
933            NetDev::Dgram(dgram) => {
934                args.append(&mut dgram.to_command());
935            }
936            NetDev::Vde(vde) => {
937                args.append(&mut vde.to_command());
938            }
939            NetDev::Netmap(netmap) => {
940                args.append(&mut netmap.to_command());
941            }
942            NetDev::AfXdp(af_xdp) => {
943                args.append(&mut af_xdp.to_command());
944            }
945            NetDev::VhostUser(vhost_user) => {
946                args.append(&mut vhost_user.to_command());
947            }
948            NetDev::VhostVdpa(vhost_vdpa) => {
949                args.append(&mut vhost_vdpa.to_command());
950            }
951            NetDev::VmnetHost(vmnet_host) => {
952                args.append(&mut vmnet_host.to_command());
953            }
954            NetDev::VmnetShared(vmnet_shared) => {
955                args.append(&mut vmnet_shared.to_command());
956            }
957            NetDev::VmnetBridged(vmnet_bridged) => {
958                args.append(&mut vmnet_bridged.to_command());
959            }
960            NetDev::Hubport(hubport) => {
961                args.append(&mut hubport.to_command());
962            }
963        }
964        cmd.push(args.join(","));
965        cmd
966    }
967}