ipset/
types.rs

1//! All the types used by libipset.
2
3use std::error::Error as StdError;
4use std::ffi::{CString, NulError};
5use std::fmt::Formatter;
6use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};
7use std::num::ParseIntError;
8
9use derive_more::{Display, From, Into};
10use ipset_derive::SetType;
11
12use crate::{binding, Session};
13
14/// list method
15pub struct ListMethod;
16
17/// bitmap method
18pub struct BitmapMethod;
19
20/// hash method
21pub struct HashMethod;
22
23/// ip data type
24/// Ip wrapper including ipv4 and ipv6
25#[derive(Copy, Clone)]
26pub enum IpDataType {
27    IPv4(libc::in_addr),
28    IPv6(libc::in6_addr),
29}
30
31impl IpDataType {
32    pub fn to_ip_addr(&self) -> IpAddr {
33        match self {
34            IpDataType::IPv4(addr) => {
35                let octets = addr.s_addr.to_ne_bytes();
36                IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]))
37            }
38            IpDataType::IPv6(addr) => {
39                let segments = [
40                    u16::from_be_bytes([addr.s6_addr[0], addr.s6_addr[1]]),
41                    u16::from_be_bytes([addr.s6_addr[2], addr.s6_addr[3]]),
42                    u16::from_be_bytes([addr.s6_addr[4], addr.s6_addr[5]]),
43                    u16::from_be_bytes([addr.s6_addr[6], addr.s6_addr[7]]),
44                    u16::from_be_bytes([addr.s6_addr[8], addr.s6_addr[9]]),
45                    u16::from_be_bytes([addr.s6_addr[10], addr.s6_addr[11]]),
46                    u16::from_be_bytes([addr.s6_addr[12], addr.s6_addr[13]]),
47                    u16::from_be_bytes([addr.s6_addr[14], addr.s6_addr[15]]),
48                ];
49                IpAddr::V6(Ipv6Addr::new(
50                    segments[0],
51                    segments[1],
52                    segments[2],
53                    segments[3],
54                    segments[4],
55                    segments[5],
56                    segments[6],
57                    segments[7],
58                ))
59            }
60        }
61    }
62}
63
64impl<T: SetType> SetData<T> for IpDataType {
65    /// get ip address pointer and ip family pointer.
66    fn set_data(&self, session: &Session<T>, from: Option<bool>) -> Result<(), Error> {
67        let (ip, family) = match self {
68            IpDataType::IPv4(ip) => (ip as *const _ as _, &binding::NFPROTO_IPV4 as *const _ as _),
69            IpDataType::IPv6(ip) => (ip as *const _ as _, &binding::NFPROTO_IPV6 as *const _ as _),
70        };
71        session.set_data(binding::ipset_opt_IPSET_OPT_FAMILY, family)?;
72        let opt = match from {
73            Some(true) => binding::ipset_opt_IPSET_OPT_IP_FROM,
74            Some(false) => binding::ipset_opt_IPSET_OPT_IP_TO,
75            None => binding::ipset_opt_IPSET_OPT_IP,
76        };
77        session.set_data(opt, ip)
78    }
79}
80
81impl Parse for IpDataType {
82    fn parse(&mut self, s: &str) -> Result<(), Error> {
83        let s = s.split(" ").next().ok_or(Error::DataParse(s.to_string()))?;
84        let ip: IpAddr = s.parse()?;
85        *self = ip.into();
86        Ok(())
87    }
88}
89
90impl Default for IpDataType {
91    fn default() -> Self {
92        IpDataType::IPv4(libc::in_addr { s_addr: 0 })
93    }
94}
95
96impl From<Ipv4Addr> for IpDataType {
97    fn from(ip: Ipv4Addr) -> Self {
98        IpDataType::IPv4(libc::in_addr {
99            s_addr: u32::from(ip).to_be(),
100        })
101    }
102}
103
104impl From<Ipv6Addr> for IpDataType {
105    fn from(ip: Ipv6Addr) -> Self {
106        IpDataType::IPv6(libc::in6_addr {
107            s6_addr: ip.octets(),
108        })
109    }
110}
111
112impl From<IpAddr> for IpDataType {
113    fn from(ip: IpAddr) -> Self {
114        match ip {
115            IpAddr::V4(v4) => v4.into(),
116            IpAddr::V6(v6) => v6.into(),
117        }
118    }
119}
120
121impl From<&IpDataType> for IpAddr {
122    fn from(value: &IpDataType) -> Self {
123        match value {
124            IpDataType::IPv4(ip) => IpAddr::from(ip.s_addr.to_ne_bytes()),
125            IpDataType::IPv6(ip) => IpAddr::from(ip.s6_addr),
126        }
127    }
128}
129
130impl Display for IpDataType {
131    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
132        let ip: IpAddr = self.into();
133        write!(f, "{}", ip)
134    }
135}
136
137/// net data type
138#[derive(Default, From, Into)]
139pub struct NetDataType {
140    ip: IpDataType,
141    cidr: u8,
142}
143
144impl NetDataType {
145    /// create net using ip and cidr
146    pub fn new(ip: impl Into<IpDataType>, cidr: u8) -> Self {
147        Self {
148            ip: ip.into(),
149            cidr,
150        }
151    }
152
153    /// return ip of the net
154    pub fn ip(&self) -> IpAddr {
155        (&self.ip).into()
156    }
157
158    /// return cidr of the net
159    pub fn cidr(&self) -> u8 {
160        self.cidr
161    }
162}
163
164impl<T: SetType> SetData<T> for NetDataType {
165    fn set_data(&self, session: &Session<T>, from: Option<bool>) -> Result<(), Error> {
166        self.ip.set_data(session, from)?;
167        session.set_data(
168            binding::ipset_opt_IPSET_OPT_CIDR,
169            &self.cidr as *const _ as _,
170        )
171    }
172}
173
174impl Parse for NetDataType {
175    fn parse(&mut self, s: &str) -> Result<(), Error> {
176        let mut ss = s.split("/");
177        if let Some(ip) = ss.next() {
178            let ip: IpAddr = ip.parse()?;
179            self.ip = ip.into();
180        }
181        if let Some(cidr) = ss.next() {
182            self.cidr = cidr.parse()?;
183        } else {
184            self.cidr = 32;
185        }
186        Ok(())
187    }
188}
189
190impl Display for NetDataType {
191    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
192        write!(f, "{}/{}", self.ip, self.cidr)
193    }
194}
195
196/// mac data type, [u8; 6]
197#[derive(Default, From, Into)]
198pub struct MacDataType {
199    mac: [u8; 6],
200}
201
202impl Parse for MacDataType {
203    fn parse(&mut self, s: &str) -> Result<(), Error> {
204        let mac: Vec<u8> = s
205            .split(":")
206            .filter_map(|s| u8::from_str_radix(s, 16).ok())
207            .collect();
208        if mac.len() != 6 {
209            Err(Error::InvalidOutput(s.into()))
210        } else {
211            self.mac.copy_from_slice(mac.as_slice());
212            Ok(())
213        }
214    }
215}
216
217impl<T: SetType> SetData<T> for MacDataType {
218    fn set_data(&self, session: &Session<T>, _from: Option<bool>) -> Result<(), Error> {
219        session.set_data(binding::ipset_opt_IPSET_OPT_ETHER, self.mac.as_ptr() as _)
220    }
221}
222
223impl Display for MacDataType {
224    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
225        let data = self.mac.map(|d| format!("{:02x}", d)).join(":");
226        write!(f, "{}", data)
227    }
228}
229
230/// port data type, u16
231#[derive(Default, From, Into)]
232pub struct PortDataType {
233    port: u16,
234}
235
236impl<T: SetType> SetData<T> for PortDataType {
237    fn set_data(&self, session: &Session<T>, from: Option<bool>) -> Result<(), Error> {
238        let opt = match from {
239            Some(true) => binding::ipset_opt_IPSET_OPT_PORT_FROM,
240            Some(false) => binding::ipset_opt_IPSET_OPT_PORT_TO,
241            None => binding::ipset_opt_IPSET_OPT_PORT,
242        };
243        session.set_data(opt, &self.port as *const _ as _)
244    }
245}
246
247impl Parse for PortDataType {
248    fn parse(&mut self, s: &str) -> Result<(), Error> {
249        self.port = s.parse()?;
250        Ok(())
251    }
252}
253
254impl Display for PortDataType {
255    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
256        write!(f, "{}", self.port)
257    }
258}
259
260/// iface data type, CString
261#[derive(Default)]
262pub struct IfaceDataType {
263    name: CString,
264}
265
266impl From<String> for IfaceDataType {
267    fn from(value: String) -> Self {
268        Self {
269            name: CString::new(value).unwrap(),
270        }
271    }
272}
273
274impl From<IfaceDataType> for String {
275    fn from(value: IfaceDataType) -> Self {
276        value.name.to_string_lossy().to_string()
277    }
278}
279
280impl Parse for IfaceDataType {
281    fn parse(&mut self, s: &str) -> Result<(), Error> {
282        self.name = CString::new(s)?;
283        Ok(())
284    }
285}
286
287impl<T: SetType> SetData<T> for IfaceDataType {
288    fn set_data(&self, session: &Session<T>, _from: Option<bool>) -> Result<(), Error> {
289        session.set_data(binding::ipset_opt_IPSET_OPT_IFACE, self.name.as_ptr() as _)
290    }
291}
292
293impl Display for IfaceDataType {
294    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
295        write!(f, "{}", self.name.to_string_lossy())
296    }
297}
298
299/// mark data type, u32
300#[derive(Default, From, Into)]
301pub struct MarkDataType {
302    mark: u32,
303}
304
305impl Parse for MarkDataType {
306    fn parse(&mut self, s: &str) -> Result<(), Error> {
307        self.mark = s.parse()?;
308        Ok(())
309    }
310}
311
312impl<T: SetType> SetData<T> for MarkDataType {
313    fn set_data(&self, session: &Session<T>, _: Option<bool>) -> Result<(), Error> {
314        session.set_data(
315            binding::ipset_opt_IPSET_OPT_MARK,
316            &self.mark as *const _ as _,
317        )
318    }
319}
320
321impl Display for MarkDataType {
322    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
323        write!(f, "{}", self.mark)
324    }
325}
326
327/// set name, CString
328#[derive(Default)]
329pub struct SetDataType {
330    name: CString,
331}
332
333impl From<String> for SetDataType {
334    fn from(value: String) -> Self {
335        Self {
336            name: CString::new(value).unwrap(),
337        }
338    }
339}
340
341impl From<SetDataType> for String {
342    fn from(value: SetDataType) -> Self {
343        value.name.to_string_lossy().to_string()
344    }
345}
346
347impl Parse for SetDataType {
348    fn parse(&mut self, s: &str) -> Result<(), Error> {
349        self.name = CString::new(s)?;
350        Ok(())
351    }
352}
353
354impl<T: SetType> SetData<T> for SetDataType {
355    fn set_data(&self, session: &Session<T>, _: Option<bool>) -> Result<(), Error> {
356        session.set_data(binding::ipset_opt_IPSET_OPT_NAME, self.name.as_ptr() as _)
357    }
358}
359
360impl Display for SetDataType {
361    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
362        write!(f, "{}", self.name.to_string_lossy())
363    }
364}
365
366macro_rules! impl_name {
367    ($($types:ident),+) => {
368        impl<$($types,)+> TypeName for ($($types),+)
369            where $($types:TypeName),+ {
370            fn name() -> String {
371                [$($types::name(),)+].join(",")
372            }
373        }
374    };
375
376    ($ty:ty, $name:expr) => {
377        impl TypeName for $ty {
378            fn name() -> String {
379                $name.into()
380            }
381        }
382    }
383}
384
385impl_name!(ListMethod, "list");
386impl_name!(BitmapMethod, "bitmap");
387impl_name!(HashMethod, "hash");
388impl_name!(IpDataType, "ip");
389impl_name!(NetDataType, "net");
390impl_name!(MacDataType, "mac");
391impl_name!(PortDataType, "port");
392impl_name!(IfaceDataType, "iface");
393impl_name!(MarkDataType, "mark");
394impl_name!(SetDataType, "set");
395impl_name!(A, B);
396impl_name!(A, B, C);
397
398macro_rules! impl_set_data {
399    ($($types:ident),+) => {
400        #[allow(non_snake_case)]
401        impl<T, $($types),+> SetData<T> for ($($types),+)
402            where T:SetType,
403                $($types:SetData<T>),+ {
404            fn set_data(&self, session:&Session<T>, from:Option<bool>) -> Result<(), Error> {
405                let ($($types),+) = self;
406                $($types.set_data(session, from)?;)+
407                Ok(())
408            }
409        }
410    };
411}
412
413impl_set_data!(A, B);
414impl_set_data!(A, B, C);
415
416macro_rules! impl_parse {
417   ($($types:ident),+) => {
418       #[allow(non_snake_case)]
419       impl<$($types),+> Parse for ($($types),+)
420            where  $($types:Parse),+ {
421            fn parse(&mut self, s:&str) -> Result<(), Error> {
422                let ($($types),+) = self;
423                let mut ss = s.split(",");
424                $(
425                    if let Some(item) = ss.next() {
426                        $types.parse(item)?;
427                    } else {
428                        return Err(Error::InvalidOutput(s.into()));
429                    };
430                )+
431                Ok(())
432            }
433        }
434    };
435}
436
437impl_parse!(A, B);
438impl_parse!(A, B, C);
439
440/// A set type comprises of the storage method by which the data is stored and the data type(s) which are stored in the set.
441/// Therefore the TYPENAME parameter  of the create command follows the syntax
442/// `TYPENAME := method:datatype[,datatype[,datatype]]`
443/// where the current list of the methods are bitmap, hash, and list and the possible data types are ip, net, mac, port and iface.
444pub trait SetType: Sized {
445    type Method;
446    type DataType: SetData<Self> + Parse + Default;
447}
448
449/// A trait used for generate name for the ipset type and method, such as ip, net, etc.
450pub trait TypeName {
451    fn name() -> String;
452}
453
454/// Set data in session for the data type.
455pub trait SetData<T: SetType> {
456    fn set_data(&self, session: &Session<T>, from: Option<bool>) -> Result<(), Error>;
457}
458
459/// parse data type from string.
460pub trait Parse {
461    fn parse(&mut self, s: &str) -> Result<(), Error>;
462}
463
464/// A trait to generate literal name for a ipset method:type composition.
465pub trait ToCString {
466    fn to_cstring() -> CString;
467}
468
469impl<T> ToCString for T
470where
471    T: SetType,
472    T::Method: TypeName,
473    T::DataType: TypeName,
474{
475    fn to_cstring() -> CString {
476        CString::new([T::Method::name(), T::DataType::name()].join(":")).unwrap()
477    }
478}
479
480/// Errors defined in this crate.
481#[derive(Debug, From, Display)]
482pub enum Error {
483    #[from(ignore)]
484    #[display("DataSet:['{}', {}", _0, _1)]
485    DataSet(String, bool),
486    #[from(ignore)]
487    #[display("Cmd:['{}', {}", _0, _1)]
488    Cmd(String, bool),
489    #[from(ignore)]
490    #[display("TypeGet:['{}', {}", _0, _1)]
491    TypeGet(String, bool),
492    #[from(ignore)]
493    InvalidOutput(String),
494    #[from(ignore)]
495    SaveRestore(String),
496    AddrParse(AddrParseError),
497    ParseInt(ParseIntError),
498    Nul(NulError),
499    #[from(ignore)]
500    CAOption(String),
501    #[from(ignore)]
502    DataParse(String),
503}
504
505impl Error {
506    pub(crate) fn cmd_contains(&self, m: &str) -> bool {
507        if let Error::Cmd(message, _) = self {
508            message.contains(m)
509        } else {
510            false
511        }
512    }
513
514    pub fn is_error(&self) -> bool {
515        match self {
516            Error::DataSet(_, error) => *error,
517            Error::Cmd(_, error) => *error,
518            Error::TypeGet(_, error) => *error,
519            _ => false,
520        }
521    }
522}
523
524impl StdError for Error {}
525
526/// The bitmap:ip set type uses a memory range to store either IPv4 host (default) or IPv4 network addresses.
527/// A bitmap:ip type of set can store up to 65536 entries.
528#[derive(SetType)]
529pub struct BitmapIp;
530
531/// The bitmap:ip,mac set type uses a memory range to store IPv4 and a MAC address pairs.
532/// A bitmap:ip,mac type of set can store up to 65536 entries.
533#[derive(SetType)]
534pub struct BitmapIpMac;
535
536/// The bitmap:port set type uses a memory range to store port numbers and such a set can store up to 65536 ports.
537#[derive(SetType)]
538pub struct BitmapPort;
539
540/// The hash:ip set type uses a hash to store IP host addresses (default) or network addresses.
541/// Zero valued IP address cannot be stored in a hash:ip type of set.
542#[derive(SetType)]
543pub struct HashIp;
544
545/// The hash:mac set type uses a hash to store MAC addresses.
546/// Zero valued MAC addresses cannot be stored in a hash:mac type of set.
547#[derive(SetType)]
548pub struct HashMac;
549
550/// The hash:ip,mac set type uses a hash to store IP and a MAC address pairs.
551/// Zero valued MAC addresses cannot be stored in a hash:ip,mac type of set.
552#[derive(SetType)]
553pub struct HashIpMac;
554
555/// The hash:net set type uses a hash to store different sized IP network addresses.  
556/// Network address with zero prefix size cannot be stored in this type of sets.
557#[derive(SetType)]
558pub struct HashNet;
559
560/// The hash:net,net set type uses a hash to store pairs of different sized IP network addresses.  
561/// Bear  in  mind  that  the  first parameter has precedence over  the second,  
562/// so a nomatch entry could be potentially be ineffective if a more specific first parameter existed with a suitable second parameter.  
563/// Network address with zero prefix size cannot be stored in this type of set.
564#[derive(SetType)]
565pub struct HashNetNet;
566
567/// The hash:ip,port set type uses a hash to store IP address and port number pairs.  
568/// The port number is interpreted together with a protocol (default TCP)  and  zero protocol number cannot be used.
569#[derive(SetType)]
570pub struct HashIpPort;
571
572/// The  hash:net,port  set  type uses a hash to store different sized IP network address and port pairs.
573/// The port number is interpreted together with a protocol (de‐fault TCP) and zero protocol number cannot be used.
574/// Network address with zero prefix size is not accepted either.
575#[derive(SetType)]
576pub struct HashNetPort;
577
578/// The hash:ip,port,ip set type uses a hash to store IP address, port number and a second IP address triples.
579/// The port number is interpreted together with a protocol (default TCP) and zero protocol number cannot be used.
580#[derive(SetType)]
581pub struct HashIpPortIp;
582
583/// The hash:ip,port,net set type uses a hash to store IP address, port number and IP network address triples.
584/// The port number is interpreted together with a protocol (default TCP) and zero protocol number cannot be used.
585/// Network address with zero prefix size cannot be stored either.
586#[derive(SetType)]
587pub struct HashIpPortNet;
588
589/// The hash:ip,mark set type uses a hash to store IP address and packet mark pairs.
590#[derive(SetType)]
591pub struct HashIpMark;
592
593/// The hash:net,port,net set type behaves similarly to hash:ip,port,net but accepts a cidr value for both the first and last parameter.
594/// Either subnet is permitted to be a /0 should you wish to match port between all destinations.
595#[derive(SetType)]
596pub struct HashNetPortNet;
597
598/// The hash:net,iface set type uses a hash to store different sized IP network address and interface name pairs.
599#[derive(SetType)]
600pub struct HashNetIface;
601
602/// The list:set type uses a simple list in which you can store set names.
603#[derive(SetType)]
604pub struct ListSet;
605
606pub trait WithNetmask {}
607
608impl WithNetmask for BitmapMethod {}
609
610impl WithNetmask for HashMethod {}
611
612#[allow(unused_imports)]
613mod tests {
614    use std::net::IpAddr;
615
616    use crate::types::{
617        BitmapIp, BitmapIpMac, BitmapPort, HashIp, HashIpMac, HashIpMark, HashIpPort, HashIpPortIp,
618        HashIpPortNet, HashMac, HashNet, HashNetIface, HashNetNet, HashNetPort, HashNetPortNet,
619        ListSet,
620    };
621    use crate::types::{
622        IfaceDataType, IpDataType, MacDataType, MarkDataType, NetDataType, Parse, PortDataType,
623        SetDataType, ToCString,
624    };
625
626    #[test]
627    fn test_ip() {
628        let ip: IpAddr = "127.0.0.1".parse().unwrap();
629        let mut data: IpDataType = ip.into();
630        let ip1: IpAddr = (&data).into();
631        assert_eq!(ip, ip1);
632        assert_eq!("127.0.0.1", format!("{}", data));
633        data.parse("192.168.3.1").unwrap();
634        assert_eq!("192.168.3.1", format!("{}", data));
635    }
636
637    #[test]
638    fn test_net() {
639        let ip: IpAddr = "127.0.0.1".parse().unwrap();
640        let mut net = NetDataType::new(ip, 8);
641        assert_eq!("127.0.0.1/8", format!("{}", net));
642        net.parse("192.168.3.1/24").unwrap();
643        assert_eq!("192.168.3.1/24", format!("{}", net));
644    }
645
646    #[test]
647    fn test_mac() {
648        let mut mac: MacDataType = [124u8, 24u8, 32u8, 129u8, 84u8, 223u8].into();
649        assert_eq!("7c:18:20:81:54:df", format!("{}", mac));
650        mac.parse("00:15:5d:37:d9:2f").unwrap();
651        assert_eq!("00:15:5d:37:d9:2f", format!("{}", mac));
652    }
653
654    #[test]
655    fn test_mark() {
656        let mut mark: MarkDataType = 32u32.into();
657        assert_eq!("32", format!("{}", mark));
658        mark.parse("123").unwrap();
659        assert_eq!("123", format!("{}", 123));
660    }
661
662    #[test]
663    fn test_port() {
664        let mut port: PortDataType = 1235u16.into();
665        assert_eq!("1235", format!("{}", port));
666        port.parse("1234").unwrap();
667        assert_eq!("1234", format!("{}", port));
668    }
669
670    #[test]
671    fn test_iface() {
672        let mut iface: IfaceDataType = String::from("abc").into();
673        assert_eq!("abc", format!("{}", iface));
674        iface.parse("test").unwrap();
675        assert_eq!("test", format!("{}", iface));
676    }
677
678    #[test]
679    fn test_set() {
680        let mut set: SetDataType = String::from("abc").into();
681        assert_eq!("abc", format!("{}", set));
682        set.parse("test").unwrap();
683        assert_eq!("test", format!("{}", set));
684    }
685
686    #[test]
687    fn test_ip_port_ip() {
688        let mut data = (
689            IpDataType::default(),
690            PortDataType::default(),
691            IpDataType::default(),
692        );
693        data.parse("192.168.3.1,8080,192.168.3.2").unwrap();
694        assert_eq!("192.168.3.1", format!("{}", data.0));
695        assert_eq!("8080", format!("{}", data.1));
696        assert_eq!("192.168.3.2", format!("{}", data.2));
697    }
698
699    #[test]
700    fn test_type_name() {
701        assert_eq!(HashIp::to_cstring().to_str().unwrap(), "hash:ip");
702        assert_eq!(
703            HashNetIface::to_cstring().to_str().unwrap(),
704            "hash:net,iface"
705        );
706        assert_eq!(HashNetNet::to_cstring().to_str().unwrap(), "hash:net,net");
707        assert_eq!(HashNetPort::to_cstring().to_str().unwrap(), "hash:net,port");
708        assert_eq!(HashNet::to_cstring().to_str().unwrap(), "hash:net");
709        assert_eq!(HashIpPort::to_cstring().to_str().unwrap(), "hash:ip,port");
710        assert_eq!(HashIpMark::to_cstring().to_str().unwrap(), "hash:ip,mark");
711        assert_eq!(
712            HashIpPortNet::to_cstring().to_str().unwrap(),
713            "hash:ip,port,net"
714        );
715        assert_eq!(HashIpMac::to_cstring().to_str().unwrap(), "hash:ip,mac");
716        assert_eq!(
717            HashIpPortIp::to_cstring().to_str().unwrap(),
718            "hash:ip,port,ip"
719        );
720        assert_eq!(
721            HashNetPortNet::to_cstring().to_str().unwrap(),
722            "hash:net,port,net"
723        );
724        assert_eq!(HashMac::to_cstring().to_str().unwrap(), "hash:mac");
725        assert_eq!(ListSet::to_cstring().to_str().unwrap(), "list:set");
726        assert_eq!(BitmapPort::to_cstring().to_str().unwrap(), "bitmap:port");
727        assert_eq!(BitmapIp::to_cstring().to_str().unwrap(), "bitmap:ip");
728        assert_eq!(BitmapIpMac::to_cstring().to_str().unwrap(), "bitmap:ip,mac");
729    }
730}
731
732/// Options which ipset supported
733pub enum EnvOption {
734    /// Sorted output. When listing or saving sets, the entries are listed sorted.
735    Sorted,
736    /// Suppress any output to stdout and stderr.  ipset will still exit with error if it cannot continue.
737    Quiet,
738    /// When listing sets, enforce name lookup. The program will try to display the IP entries resolved to host names which requires slow DNS lookups.
739    Resolve,
740    /// Ignore errors when exactly the same set is to be created or already added entry is added or missing entry is deleted
741    Exist,
742    /// List just the names of the existing sets, i.e. suppress listing of set headers and members.
743    ListSetName,
744    /// List the set names and headers, i.e. suppress listing of set members.
745    ListHeader,
746}
747
748impl EnvOption {
749    /// convert to bindings type.
750    pub(crate) fn to_option(self) -> binding::ipset_envopt {
751        match self {
752            EnvOption::Sorted => binding::ipset_envopt_IPSET_ENV_SORTED,
753            EnvOption::Quiet => binding::ipset_envopt_IPSET_ENV_QUIET,
754            EnvOption::Resolve => binding::ipset_envopt_IPSET_ENV_RESOLVE,
755            EnvOption::Exist => binding::ipset_envopt_IPSET_ENV_EXIST,
756            EnvOption::ListSetName => binding::ipset_envopt_IPSET_ENV_LIST_SETNAME,
757            EnvOption::ListHeader => binding::ipset_envopt_IPSET_ENV_LIST_HEADER,
758        }
759    }
760}
761
762/// Options for creation and addition.
763#[derive(Debug)]
764pub enum AddOption {
765    /// The value of the timeout parameter for the create command means the default timeout value
766    /// (in seconds) for new entries. If a set is created with timeout support, then the same
767    /// timeout option can  be  used  to  specify  non-default timeout  values when adding entries.
768    /// Zero timeout value means the entry is added permanent to the set.
769    Timeout(u32),
770    /// bytes counter for the element.
771    Bytes(u64),
772    /// packets counter for the element.
773    Packets(u64),
774    /// skbmark option format: MARK or MARK/MASK, where MARK and  MASK  are  32bit  hex
775    /// numbers  with  0x  prefix. If only mark is specified mask 0xffffffff are used.
776    SkbMark(u32, u32),
777    /// skbprio option has tc class format: MAJOR:MINOR, where major and minor numbers are
778    /// hex without 0x prefix.
779    SkbPrio(u16, u16),
780    /// skbqueue option is just decimal number.
781    SkbQueue(u16),
782    /// All set types support the optional comment extension.  Enabling this extension on an ipset
783    /// enables you to annotate an ipset entry  with  an  arbitrary  string. This  string is
784    /// completely ignored by both the kernel and ipset itself and is purely for providing a
785    /// convenient means to document the reason for an entry's existence. Comments must not contain
786    /// any quotation marks and the usual escape character (\) has no meaning
787    Comment(String),
788    /// The  hash  set  types which can store net type of data (i.e. hash:*net*) support the
789    /// optional nomatch option when adding entries. When matching elements in the set, entries
790    /// marked as nomatch are skipped as if those were not added to the set, which makes possible
791    /// to build up sets with exceptions.  See  the  example  at hash type hash:net below.
792    /// When  elements  are  tested  by ipset, the nomatch flags are taken into account.
793    /// If one wants to test the existence of an element marked with nomatch in a set,
794    /// then the flag must be specified too.
795    Nomatch,
796}
797
798pub struct NormalListResult<T: SetType> {
799    pub name: String,
800    pub typ: String,
801    pub revision: u32,
802    pub header: ListHeader,
803    pub size_in_memory: u32,
804    pub references: u32,
805    pub entry_size: u32,
806    pub items: Option<Vec<(T::DataType, Option<Vec<AddOption>>)>>,
807}
808
809impl<T: SetType> Default for NormalListResult<T> {
810    fn default() -> Self {
811        Self {
812            name: "".to_string(),
813            typ: "".to_string(),
814            revision: 0,
815            header: Default::default(),
816            size_in_memory: 0,
817            references: 0,
818            entry_size: 0,
819            items: None,
820        }
821    }
822}
823
824pub enum ListResult<T: SetType> {
825    Normal(NormalListResult<T>),
826    Terse(Vec<String>),
827}
828
829impl<T: SetType> NormalListResult<T> {
830    pub(crate) fn update_from_str(&mut self, line: &str) -> Result<(), Error> {
831        if self.items.is_none() {
832            let fields: Vec<_> = line.splitn(2, ":").collect();
833            match fields[0] {
834                "Name" => {
835                    self.name = fields[1].trim().to_string();
836                }
837                "Type" => {
838                    self.typ = fields[1].trim().to_string();
839                }
840                "Revision" => {
841                    self.revision = fields[1].trim().parse()?;
842                }
843                "Header" => {
844                    self.header = ListHeader::from_str(fields[1].trim());
845                }
846                "Size in memory" => {
847                    self.size_in_memory = fields[1].trim().parse()?;
848                }
849                "References" => {
850                    self.references = fields[1].trim().parse()?;
851                }
852                "Number of entries" => {
853                    self.entry_size = fields[1].trim().parse()?;
854                }
855                "Members" => {
856                    self.items = Some(Vec::new());
857                }
858                _ => {
859                    unreachable!("unexpected {}", fields[0])
860                }
861            }
862        } else {
863            let fields: Vec<_> = line.split_ascii_whitespace().collect();
864            let mut data = T::DataType::default();
865            let mut add_options = None;
866            if fields.len() == 0 || data.parse(fields[0]).is_err() {
867                return Err(Error::InvalidOutput(String::from(line)));
868            } else if fields.len() > 1 {
869                let mut i = 1;
870                let mut options = vec![];
871                while i < fields.len() {
872                    match fields[i] {
873                        "timeout" => {
874                            options.push(AddOption::Timeout(fields[i + 1].parse()?));
875                        }
876                        "packets" => {
877                            options.push(AddOption::Packets(fields[i + 1].parse()?));
878                        }
879                        "bytes" => {
880                            options.push(AddOption::Bytes(fields[i + 1].trim().replace("\0", "").parse()?));
881                        }
882                        "comment" => {
883                            options.push(AddOption::Comment(fields[i + 1].to_string()));
884                        }
885                        "skbmark" => {
886                            let values: Vec<_> = fields[i + 1].split('/').collect();
887                            let v0 =
888                                u32::from_str_radix(values[0].strip_prefix("0x").unwrap(), 16)?;
889                            let v1 = if values.len() > 1 {
890                                u32::from_str_radix(values[1].strip_prefix("0x").unwrap(), 16)?
891                            } else {
892                                u32::MAX
893                            };
894                            options.push(AddOption::SkbMark(v0, v1));
895                        }
896                        "skbprio" => {
897                            let values: Vec<_> = fields[i + 1].split(':').collect();
898                            let v0 = u16::from_str_radix(values[0], 16)?;
899                            let v1 = u16::from_str_radix(values[1], 16)?;
900                            options.push(AddOption::SkbPrio(v0, v1));
901                        }
902                        "skbqueue" => {
903                            options.push(AddOption::SkbQueue(fields[i + 1].parse()?));
904                        }
905                        "nomatch" => {
906                            options.push(AddOption::Nomatch);
907                            i += 1;
908                            continue;
909                        }
910                        _ => {
911                            unreachable!("{} not supported", fields[i]);
912                        }
913                    }
914                    i += 2
915                }
916                add_options = Some(options);
917            }
918            self.items.as_mut().unwrap().push((data, add_options));
919        }
920        Ok(())
921    }
922}
923
924#[derive(Default, Debug)]
925pub struct ListHeader {
926    ipv6: bool,
927    hash_size: u32,
928    bucket_size: Option<u32>,
929    max_elem: u32,
930    counters: bool,
931    comment: bool,
932    skbinfo: bool,
933    initval: Option<u32>
934}
935
936impl ListHeader {
937    pub fn from_str(s: &str) -> Self {
938        let s: Vec<_> = s.split_whitespace().collect();
939        let mut header = ListHeader::default();
940        let mut i = 0;
941        while i < s.len() {
942            match s[i] {
943                "family" => {
944                    header.ipv6 = s[i + 1] == "inet6";
945                    i += 2;
946                }
947                "hashsize" => {
948                    header.hash_size = s[i + 1].parse().unwrap();
949                    i += 2;
950                }
951                "bucketsize" => {
952                    header.bucket_size = Some(s[i + 1].parse().unwrap());
953                    i += 2;
954                },
955                "maxelem" => {
956                    header.max_elem = s[i + 1].parse().unwrap();
957                    i += 2;
958                }
959                "counters" => {
960                    header.counters = true;
961                    i += 1;
962                }
963                "comment" => {
964                    header.comment = true;
965                    i += 1;
966                }
967                "skbinfo" => {
968                    header.skbinfo = true;
969                    i += 1;
970                }
971                "initval" => {
972                    if let Some(initval) = s[i + 1].strip_prefix("0x") {
973                        header.initval = Some(u32::from_str_radix(initval, 16).unwrap());
974                    }
975                    i += 2;
976                }
977
978                _ => {
979                    unreachable!("{} not supported", s[i]);
980                }
981            }
982        }
983        header
984    }
985}