radius_rust/protocol/
dictionary.rs

1//! RADIUS Dictionary implementation
2
3
4use std::fs::File;
5use std::io::{self, BufRead};
6
7use super::error::RadiusError;
8
9#[derive(Debug, PartialEq)]
10/// Represents a list of supported data types
11/// as defined in RFC 2865 & RFC 8044
12pub enum SupportedAttributeTypes {
13    /// Rust's String; RFC 8044 calls this "text" - UTF-8 text
14    AsciiString,
15    /// Rusts's [u8]; RFC 8044 calls this "string" (FreeRADIUS calls this "octets") - binary data as a sequence of undistinguished octets
16    ByteString,
17    /// Rust's u32
18    Integer,
19    /// Rust's u64
20    Integer64,
21    /// Rust's u32; RFC 8044 calls this "time"
22    Date,
23    /// Rust's \[u8;4\]
24    IPv4Addr,
25    /// Rust's \[u8;5\]
26    IPv4Prefix,
27    /// Rust's \[u8;16\]
28    IPv6Addr,
29    /// Rust's \[u8;18\]
30    IPv6Prefix,
31    /// Rust's \[u8;8\]; RFC 8044 calls this "ifid"
32    InterfaceId,
33    /// Rust's u32
34    Enum,
35    /// Rust's [u8]
36    Tlv,
37    /// Rust's [u8]; RFC 8044 defines this as vendor-specific data
38    Vsa,
39    /// Rust's [u8]; RFC 8044 defines this as Extended-Vendor-Specific Attribute (FreeRADIUS
40    /// accepts VSA instead of EVS data type)
41    Evs,
42    /// Rust's [u8]; Doesn't look like a type on its own, but rather an extension to some data types (in FreeRADIUS this is a flag)
43    /// usually string/octets
44    Concat,
45    /// Rust's [u8]; Doesn't look like a type on its own, but rather an extension to some data types (in FreeRADIUS this is a flag)
46    Extended,
47    /// Rust's [u8]; Doesn't look like a type on its own, but rather an extension to some data types (in FreeRADIUS this is a flag)
48    LongExtended
49}
50
51
52#[derive(Debug, PartialEq)]
53/// Represents an ATTRIBUTE from RADIUS dictionary file
54pub struct DictionaryAttribute {
55    /*
56     * |--------|   name  | code | code type |
57     * ATTRIBUTE User-Name   1      string
58     */
59    name:        String,
60    vendor_name: String,
61    code:        u8,
62    code_type:   Option<SupportedAttributeTypes>
63}
64
65impl DictionaryAttribute {
66    /// Return name of the Attribute
67    pub fn name(&self) -> &str {
68        &self.name
69    }
70
71    /// Return code of the Attribute
72    pub fn code(&self) -> u8 {
73        self.code
74    }
75
76    /// Return code_type of the Attribute
77    pub fn code_type(&self) -> &Option<SupportedAttributeTypes> {
78        &self.code_type
79    }
80}
81
82
83#[derive(Debug, PartialEq)]
84/// Represents a VALUE from RADIUS dictionary file
85pub struct DictionaryValue {
86    attribute_name: String,
87    value_name:     String,
88    vendor_name:    String,
89    value:          String
90}
91
92impl DictionaryValue {
93    /// Return name of the Value
94    pub fn name(&self) -> &str {
95        &self.value_name
96    }
97
98    /// Return attribute_name of the Value
99    pub fn attribute_name(&self) -> &str {
100        &self.attribute_name
101    }
102
103    /// Return value of the Value
104    pub fn value(&self) -> &str {
105        &self.value
106    }
107}
108
109
110#[derive(Debug, PartialEq)]
111/// Represents a VENDOR from RADIUS dictionary file
112pub struct DictionaryVendor {
113    name: String,
114    id:   u8
115}
116
117
118const COMMENT_PREFIX: &str = "#";
119
120#[derive(Debug, Default, PartialEq)]
121/// Represents RADIUS dictionary
122pub struct Dictionary {
123    attributes: Vec<DictionaryAttribute>,
124    values:     Vec<DictionaryValue>,
125    vendors:    Vec<DictionaryVendor>
126}
127
128#[allow(unused)]
129impl Dictionary {
130    /// Creates Dictionary from a RADIUS dictionary string
131    pub fn from_str(dictionary_str: &str) -> Result<Dictionary, RadiusError> {
132        let lines = read_str(dictionary_str);
133        Dictionary::from_lines(lines)
134    }
135
136    /// Creates Dictionary from a RADIUS dictionary file
137    pub fn from_file(file_path: &str) -> Result<Dictionary, RadiusError> {
138        match read_file(file_path) {
139            Ok(lines) => Dictionary::from_lines(lines),
140            Err(error) => Err(error)
141        }
142    }
143
144    /// Processes attributes, values and vendors from a supplied dictionary string and
145    /// adds those to attributes, values and vendors of an existing Dictionary
146    pub fn add_str(&mut self, dictionary_str: &str) -> Result<(), RadiusError> {
147        let lines = read_str(dictionary_str);
148        parse_lines(lines, &mut self.attributes, &mut self.values, &mut self.vendors)
149    }
150  
151    /// Processes attributes, values and vendors from a supplied dictionary file and
152    /// adds those to attributes, values and vendors of an existing Dictionary
153    pub fn add_file(&mut self, file_path: &str) -> Result<(), RadiusError> {
154        match read_file(file_path) {
155            Ok(lines) => parse_lines(
156                lines, &mut self.attributes, &mut self.values, &mut self.vendors
157            ),
158            Err(error) => Err(error)
159        }
160    }
161
162    /// Returns parsed DictionaryAttributes
163    pub fn attributes(&self) -> &[DictionaryAttribute] {
164        &self.attributes
165    }
166
167    /// Returns parsed DictionaryValues
168    pub fn values(&self) -> &[DictionaryValue] {
169        &self.values
170    }
171
172    /// Returns parsed DictionaryVendors
173    pub fn vendors(&self) -> &[DictionaryVendor] {
174        &self.vendors
175    }
176
177    fn from_lines(lines: StringIterator) -> Result<Dictionary, RadiusError> {
178        let mut attributes:  Vec<DictionaryAttribute> = Vec::new();
179        let mut values:      Vec<DictionaryValue>     = Vec::new();
180        let mut vendors:     Vec<DictionaryVendor>    = Vec::new();
181
182        match parse_lines(lines, &mut attributes, &mut values, &mut vendors) {
183            Ok(()) => Ok(Dictionary { attributes, values, vendors }),
184            Err(error) => Err(error),
185        }
186    }
187}
188
189fn assign_attribute_type(code_type: &str) -> Option<SupportedAttributeTypes> {
190    match code_type {
191        "text"          => Some(SupportedAttributeTypes::AsciiString),
192        "string"        => Some(SupportedAttributeTypes::ByteString),
193        "integer"       => Some(SupportedAttributeTypes::Integer),
194        "integer64"     => Some(SupportedAttributeTypes::Integer64),
195        "time"          => Some(SupportedAttributeTypes::Date),
196        "ipv4addr"      => Some(SupportedAttributeTypes::IPv4Addr),
197        "ipv4prefix"    => Some(SupportedAttributeTypes::IPv4Prefix),
198        "ipv6addr"      => Some(SupportedAttributeTypes::IPv6Addr),
199        "ipv6prefix"    => Some(SupportedAttributeTypes::IPv6Prefix),
200        "ifid"          => Some(SupportedAttributeTypes::InterfaceId),
201        "enum"          => Some(SupportedAttributeTypes::Enum),
202        "tlv"           => Some(SupportedAttributeTypes::Tlv),
203        "vsa"           => Some(SupportedAttributeTypes::Vsa),
204        "evs"           => Some(SupportedAttributeTypes::Evs),
205        "concat"        => Some(SupportedAttributeTypes::Concat),
206        "extended"      => Some(SupportedAttributeTypes::Extended),
207        "long-extended" => Some(SupportedAttributeTypes::LongExtended),
208        _               => None
209    }
210}
211
212type StringIterator = Box<dyn Iterator<Item = String>>;
213
214fn filter_lines<T: Iterator<Item = String> + 'static>(lines: T) -> StringIterator {
215    Box::new(
216        lines
217            .filter(|line| !line.is_empty())
218            .filter(|line| !line.contains(&COMMENT_PREFIX))
219    )
220}
221
222fn read_file(file_path: &str) -> Result<StringIterator, RadiusError> {
223    let reader = io::BufReader::new(File::open(file_path).map_err(|error| RadiusError::MalformedDictionaryError { error })?);
224    Ok(filter_lines(reader.lines().filter_map(Result::ok)))
225}
226
227fn read_str(dictionary_str: &str) -> StringIterator {
228    let lines: Vec<String> = dictionary_str.to_string().lines()
229            .map(|line| line.to_owned()).collect();
230    filter_lines(lines.into_iter())
231}
232
233fn parse_lines(lines: StringIterator, attributes: &mut Vec<DictionaryAttribute>, values: &mut Vec<DictionaryValue>, vendors: &mut Vec<DictionaryVendor>) -> Result<(), RadiusError>{
234    let mut vendor_name: String = String::new();
235
236    for line in lines {
237        let parsed_line: Vec<&str> = line.split_whitespace().filter(|&item| !item.is_empty()).collect();
238        match parsed_line[0] {
239            "ATTRIBUTE"    => parse_attribute(parsed_line, &vendor_name, attributes),
240            "VALUE"        => parse_value(parsed_line, &vendor_name, values),
241            "VENDOR"       => parse_vendor(parsed_line, vendors),
242            "BEGIN-VENDOR" => { vendor_name.insert_str(0, parsed_line[1]) },
243            "END-VENDOR"   => { vendor_name.clear() },
244            _              => continue
245        }
246    };
247
248    Ok(())
249}
250
251fn parse_attribute(parsed_line: Vec<&str>, vendor_name: &str, attributes: &mut Vec<DictionaryAttribute>) {
252    if let Ok(code) = parsed_line[2].parse::<u8>() {
253        attributes.push(DictionaryAttribute {
254            name:        parsed_line[1].to_string(),
255            vendor_name: vendor_name.to_string(),
256            code,
257            code_type:   assign_attribute_type(parsed_line[3])
258        });
259    }
260}
261
262fn parse_value(parsed_line: Vec<&str>, vendor_name: &str, values: &mut Vec<DictionaryValue>) {
263    values.push(DictionaryValue {
264        attribute_name: parsed_line[1].to_string(),
265        value_name:     parsed_line[2].to_string(),
266        vendor_name:    vendor_name.to_string(),
267        value:          parsed_line[3].to_string()
268    })
269}
270
271fn parse_vendor(parsed_line: Vec<&str>, vendors: &mut Vec<DictionaryVendor>) {
272    if let Ok(id) = parsed_line[2].parse::<u8>() {
273        vendors.push(DictionaryVendor {
274            name: parsed_line[1].to_string(),
275            id,
276        })
277    }
278}
279
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn test_from_str() {
287        let dictionary_str = include_str!("../../dict_examples/test_dictionary_dict");
288
289        let dict = Dictionary::from_str(dictionary_str).unwrap();
290
291        let mut attributes: Vec<DictionaryAttribute> = Vec::new();
292        attributes.push(DictionaryAttribute {
293            name:        "User-Name".to_string(),
294            vendor_name: "".to_string(),
295            code:        1,
296            code_type:   Some(SupportedAttributeTypes::AsciiString)
297        });
298        attributes.push(DictionaryAttribute {
299            name:        "NAS-IP-Address".to_string(),
300            vendor_name: "".to_string(),
301            code:        4,
302            code_type:   Some(SupportedAttributeTypes::IPv4Addr)
303        });
304        attributes.push(DictionaryAttribute {
305            name:        "NAS-Port-Id".to_string(),
306            vendor_name: "".to_string(),
307            code:        5,
308            code_type:   Some(SupportedAttributeTypes::Integer)
309        });
310        attributes.push(DictionaryAttribute {
311            name:        "Framed-Protocol".to_string(),
312            vendor_name: "".to_string(),
313            code:        7,
314            code_type:   Some(SupportedAttributeTypes::Integer)
315        });
316        attributes.push(DictionaryAttribute {
317            name:        "Chargeable-User-Identity".to_string(),
318            vendor_name: "".to_string(),
319            code:        89,
320            code_type:   Some(SupportedAttributeTypes::ByteString)
321        });
322        attributes.push(DictionaryAttribute {
323            name:        "Delegated-IPv6-Prefix".to_string(),
324            vendor_name: "".to_string(),
325            code:        123,
326            code_type:   Some(SupportedAttributeTypes::IPv6Prefix)
327        });
328        attributes.push(DictionaryAttribute {
329            name:        "MIP6-Feature-Vector".to_string(),
330            vendor_name: "".to_string(),
331            code:        124,
332            code_type:   Some(SupportedAttributeTypes::Integer64)
333        });
334        attributes.push(DictionaryAttribute {
335            name:        "Mobile-Node-Identifier".to_string(),
336            vendor_name: "".to_string(),
337            code:        145,
338            code_type:   Some(SupportedAttributeTypes::ByteString)
339        });
340        attributes.push(DictionaryAttribute {
341            name:        "PMIP6-Home-Interface-ID".to_string(),
342            vendor_name: "".to_string(),
343            code:        153,
344            code_type:   Some(SupportedAttributeTypes::InterfaceId)
345        });
346        attributes.push(DictionaryAttribute {
347            name:        "PMIP6-Home-IPv4-HoA".to_string(),
348            vendor_name: "".to_string(),
349            code:        155,
350            code_type:   Some(SupportedAttributeTypes::IPv4Prefix)
351        });
352        attributes.push(DictionaryAttribute {
353            name:        "Somevendor-Name".to_string(),
354            vendor_name: "Somevendor".to_string(),
355            code:        1,
356            code_type:   Some(SupportedAttributeTypes::AsciiString)
357        });
358        attributes.push(DictionaryAttribute {
359            name:        "Somevendor-Number".to_string(),
360            vendor_name: "Somevendor".to_string(),
361            code:        2,
362            code_type:   Some(SupportedAttributeTypes::Integer)
363        });
364        attributes.push(DictionaryAttribute {
365            name:        "Class".to_string(),
366            vendor_name: "".to_string(),
367            code:        25,
368            code_type:   Some(SupportedAttributeTypes::ByteString)
369        });
370
371        let mut values: Vec<DictionaryValue> = Vec::new();
372        values.push(DictionaryValue {
373            attribute_name: "Framed-Protocol".to_string(),
374            value_name:     "PPP".to_string(),
375            vendor_name:    "".to_string(),
376            value:          "1".to_string()
377        });
378        values.push(DictionaryValue {
379            attribute_name: "Somevendor-Number".to_string(),
380            value_name:     "Two".to_string(),
381            vendor_name:    "Somevendor".to_string(),
382            value:          "2".to_string()
383        });
384
385        let mut vendors: Vec<DictionaryVendor> = Vec::new();
386        vendors.push(DictionaryVendor {
387            name: "Somevendor".to_string(),
388            id:   10,
389        });
390
391        let expected_dict = Dictionary { attributes, values, vendors };
392        assert_eq!(dict, expected_dict)
393    }
394
395    #[test]
396    fn test_from_file() {
397        let dictionary_path = "./dict_examples/test_dictionary_dict";
398
399        let dict = Dictionary::from_file(dictionary_path).unwrap();
400
401        let mut attributes: Vec<DictionaryAttribute> = Vec::new();
402        attributes.push(DictionaryAttribute {
403            name:        "User-Name".to_string(),
404            vendor_name: "".to_string(),
405            code:        1,
406            code_type:   Some(SupportedAttributeTypes::AsciiString)
407        });
408        attributes.push(DictionaryAttribute {
409            name:        "NAS-IP-Address".to_string(),
410            vendor_name: "".to_string(),
411            code:        4,
412            code_type:   Some(SupportedAttributeTypes::IPv4Addr)
413        });
414        attributes.push(DictionaryAttribute {
415            name:        "NAS-Port-Id".to_string(),
416            vendor_name: "".to_string(),
417            code:        5,
418            code_type:   Some(SupportedAttributeTypes::Integer)
419        });
420        attributes.push(DictionaryAttribute {
421            name:        "Framed-Protocol".to_string(),
422            vendor_name: "".to_string(),
423            code:        7,
424            code_type:   Some(SupportedAttributeTypes::Integer)
425        });
426        attributes.push(DictionaryAttribute {
427            name:        "Chargeable-User-Identity".to_string(),
428            vendor_name: "".to_string(),
429            code:        89,
430            code_type:   Some(SupportedAttributeTypes::ByteString)
431        });
432        attributes.push(DictionaryAttribute {
433            name:        "Delegated-IPv6-Prefix".to_string(),
434            vendor_name: "".to_string(),
435            code:        123,
436            code_type:   Some(SupportedAttributeTypes::IPv6Prefix)
437        });
438        attributes.push(DictionaryAttribute {
439            name:        "MIP6-Feature-Vector".to_string(),
440            vendor_name: "".to_string(),
441            code:        124,
442            code_type:   Some(SupportedAttributeTypes::Integer64)
443        });
444        attributes.push(DictionaryAttribute {
445            name:        "Mobile-Node-Identifier".to_string(),
446            vendor_name: "".to_string(),
447            code:        145,
448            code_type:   Some(SupportedAttributeTypes::ByteString)
449        });
450        attributes.push(DictionaryAttribute {
451            name:        "PMIP6-Home-Interface-ID".to_string(),
452            vendor_name: "".to_string(),
453            code:        153,
454            code_type:   Some(SupportedAttributeTypes::InterfaceId)
455        });
456        attributes.push(DictionaryAttribute {
457            name:        "PMIP6-Home-IPv4-HoA".to_string(),
458            vendor_name: "".to_string(),
459            code:        155,
460            code_type:   Some(SupportedAttributeTypes::IPv4Prefix)
461        });
462        attributes.push(DictionaryAttribute {
463            name:        "Somevendor-Name".to_string(),
464            vendor_name: "Somevendor".to_string(),
465            code:        1,
466            code_type:   Some(SupportedAttributeTypes::AsciiString)
467        });
468        attributes.push(DictionaryAttribute {
469            name:        "Somevendor-Number".to_string(),
470            vendor_name: "Somevendor".to_string(),
471            code:        2,
472            code_type:   Some(SupportedAttributeTypes::Integer)
473        });
474        attributes.push(DictionaryAttribute {
475            name:        "Class".to_string(),
476            vendor_name: "".to_string(),
477            code:        25,
478            code_type:   Some(SupportedAttributeTypes::ByteString)
479        });
480
481        let mut values: Vec<DictionaryValue> = Vec::new();
482        values.push(DictionaryValue {
483            attribute_name: "Framed-Protocol".to_string(),
484            value_name:     "PPP".to_string(),
485            vendor_name:    "".to_string(),
486            value:          "1".to_string()
487        });
488        values.push(DictionaryValue {
489            attribute_name: "Somevendor-Number".to_string(),
490            value_name:     "Two".to_string(),
491            vendor_name:    "Somevendor".to_string(),
492            value:          "2".to_string()
493        });
494
495        let mut vendors: Vec<DictionaryVendor> = Vec::new();
496        vendors.push(DictionaryVendor {
497            name: "Somevendor".to_string(),
498            id:   10,
499        });
500
501        let expected_dict = Dictionary { attributes, values, vendors };
502        assert_eq!(dict, expected_dict)
503    }
504
505    #[test]
506    fn test_add_str() {
507        let empty_dictionary_str = include_str!("../../dict_examples/empty_test_dictionary_dict");
508        let dictionary_str       = include_str!("../../dict_examples/test_dictionary_dict");
509
510        let mut dict = Dictionary::from_str(empty_dictionary_str).unwrap();
511        dict.add_str(dictionary_str).unwrap();
512
513        let mut attributes: Vec<DictionaryAttribute> = Vec::new();
514        attributes.push(DictionaryAttribute {
515            name:        "User-Name".to_string(),
516            vendor_name: "".to_string(),
517            code:        1,
518            code_type:   Some(SupportedAttributeTypes::AsciiString)
519        });
520        attributes.push(DictionaryAttribute {
521            name:        "NAS-IP-Address".to_string(),
522            vendor_name: "".to_string(),
523            code:        4,
524            code_type:   Some(SupportedAttributeTypes::IPv4Addr)
525        });
526        attributes.push(DictionaryAttribute {
527            name:        "NAS-Port-Id".to_string(),
528            vendor_name: "".to_string(),
529            code:        5,
530            code_type:   Some(SupportedAttributeTypes::Integer)
531        });
532        attributes.push(DictionaryAttribute {
533            name:        "Framed-Protocol".to_string(),
534            vendor_name: "".to_string(),
535            code:        7,
536            code_type:   Some(SupportedAttributeTypes::Integer)
537        });
538        attributes.push(DictionaryAttribute {
539            name:        "Chargeable-User-Identity".to_string(),
540            vendor_name: "".to_string(),
541            code:        89,
542            code_type:   Some(SupportedAttributeTypes::ByteString)
543        });
544        attributes.push(DictionaryAttribute {
545            name:        "Delegated-IPv6-Prefix".to_string(),
546            vendor_name: "".to_string(),
547            code:        123,
548            code_type:   Some(SupportedAttributeTypes::IPv6Prefix)
549        });
550        attributes.push(DictionaryAttribute {
551            name:        "MIP6-Feature-Vector".to_string(),
552            vendor_name: "".to_string(),
553            code:        124,
554            code_type:   Some(SupportedAttributeTypes::Integer64)
555        });
556        attributes.push(DictionaryAttribute {
557            name:        "Mobile-Node-Identifier".to_string(),
558            vendor_name: "".to_string(),
559            code:        145,
560            code_type:   Some(SupportedAttributeTypes::ByteString)
561        });
562        attributes.push(DictionaryAttribute {
563            name:        "PMIP6-Home-Interface-ID".to_string(),
564            vendor_name: "".to_string(),
565            code:        153,
566            code_type:   Some(SupportedAttributeTypes::InterfaceId)
567        });
568        attributes.push(DictionaryAttribute {
569            name:        "PMIP6-Home-IPv4-HoA".to_string(),
570            vendor_name: "".to_string(),
571            code:        155,
572            code_type:   Some(SupportedAttributeTypes::IPv4Prefix)
573        });
574        attributes.push(DictionaryAttribute {
575            name:        "Somevendor-Name".to_string(),
576            vendor_name: "Somevendor".to_string(),
577            code:        1,
578            code_type:   Some(SupportedAttributeTypes::AsciiString)
579        });
580        attributes.push(DictionaryAttribute {
581            name:        "Somevendor-Number".to_string(),
582            vendor_name: "Somevendor".to_string(),
583            code:        2,
584            code_type:   Some(SupportedAttributeTypes::Integer)
585        });
586        attributes.push(DictionaryAttribute {
587            name:        "Class".to_string(),
588            vendor_name: "".to_string(),
589            code:        25,
590            code_type:   Some(SupportedAttributeTypes::ByteString)
591        });
592
593        let mut values: Vec<DictionaryValue> = Vec::new();
594        values.push(DictionaryValue {
595            attribute_name: "Framed-Protocol".to_string(),
596            value_name:     "PPP".to_string(),
597            vendor_name:    "".to_string(),
598            value:          "1".to_string()
599        });
600        values.push(DictionaryValue {
601            attribute_name: "Somevendor-Number".to_string(),
602            value_name:     "Two".to_string(),
603            vendor_name:    "Somevendor".to_string(),
604            value:          "2".to_string()
605        });
606
607        let mut vendors: Vec<DictionaryVendor> = Vec::new();
608        vendors.push(DictionaryVendor {
609            name: "Somevendor".to_string(),
610            id:   10,
611        });
612
613        let expected_dict = Dictionary { attributes, values, vendors };
614        assert_eq!(dict, expected_dict)
615    }
616
617    #[test]
618    fn test_add_file() {
619        let empty_dictionary_path = "./dict_examples/empty_test_dictionary_dict";
620        let dictionary_path       = "./dict_examples/test_dictionary_dict";
621
622        let mut dict = Dictionary::from_file(empty_dictionary_path).unwrap();
623        dict.add_file(dictionary_path).unwrap();
624
625        let mut attributes: Vec<DictionaryAttribute> = Vec::new();
626        attributes.push(DictionaryAttribute {
627            name:        "User-Name".to_string(),
628            vendor_name: "".to_string(),
629            code:        1,
630            code_type:   Some(SupportedAttributeTypes::AsciiString)
631        });
632        attributes.push(DictionaryAttribute {
633            name:        "NAS-IP-Address".to_string(),
634            vendor_name: "".to_string(),
635            code:        4,
636            code_type:   Some(SupportedAttributeTypes::IPv4Addr)
637        });
638        attributes.push(DictionaryAttribute {
639            name:        "NAS-Port-Id".to_string(),
640            vendor_name: "".to_string(),
641            code:        5,
642            code_type:   Some(SupportedAttributeTypes::Integer)
643        });
644        attributes.push(DictionaryAttribute {
645            name:        "Framed-Protocol".to_string(),
646            vendor_name: "".to_string(),
647            code:        7,
648            code_type:   Some(SupportedAttributeTypes::Integer)
649        });
650        attributes.push(DictionaryAttribute {
651            name:        "Chargeable-User-Identity".to_string(),
652            vendor_name: "".to_string(),
653            code:        89,
654            code_type:   Some(SupportedAttributeTypes::ByteString)
655        });
656        attributes.push(DictionaryAttribute {
657            name:        "Delegated-IPv6-Prefix".to_string(),
658            vendor_name: "".to_string(),
659            code:        123,
660            code_type:   Some(SupportedAttributeTypes::IPv6Prefix)
661        });
662        attributes.push(DictionaryAttribute {
663            name:        "MIP6-Feature-Vector".to_string(),
664            vendor_name: "".to_string(),
665            code:        124,
666            code_type:   Some(SupportedAttributeTypes::Integer64)
667        });
668        attributes.push(DictionaryAttribute {
669            name:        "Mobile-Node-Identifier".to_string(),
670            vendor_name: "".to_string(),
671            code:        145,
672            code_type:   Some(SupportedAttributeTypes::ByteString)
673        });
674        attributes.push(DictionaryAttribute {
675            name:        "PMIP6-Home-Interface-ID".to_string(),
676            vendor_name: "".to_string(),
677            code:        153,
678            code_type:   Some(SupportedAttributeTypes::InterfaceId)
679        });
680        attributes.push(DictionaryAttribute {
681            name:        "PMIP6-Home-IPv4-HoA".to_string(),
682            vendor_name: "".to_string(),
683            code:        155,
684            code_type:   Some(SupportedAttributeTypes::IPv4Prefix)
685        });
686        attributes.push(DictionaryAttribute {
687            name:        "Somevendor-Name".to_string(),
688            vendor_name: "Somevendor".to_string(),
689            code:        1,
690            code_type:   Some(SupportedAttributeTypes::AsciiString)
691        });
692        attributes.push(DictionaryAttribute {
693            name:        "Somevendor-Number".to_string(),
694            vendor_name: "Somevendor".to_string(),
695            code:        2,
696            code_type:   Some(SupportedAttributeTypes::Integer)
697        });
698        attributes.push(DictionaryAttribute {
699            name:        "Class".to_string(),
700            vendor_name: "".to_string(),
701            code:        25,
702            code_type:   Some(SupportedAttributeTypes::ByteString)
703        });
704
705        let mut values: Vec<DictionaryValue> = Vec::new();
706        values.push(DictionaryValue {
707            attribute_name: "Framed-Protocol".to_string(),
708            value_name:     "PPP".to_string(),
709            vendor_name:    "".to_string(),
710            value:          "1".to_string()
711        });
712        values.push(DictionaryValue {
713            attribute_name: "Somevendor-Number".to_string(),
714            value_name:     "Two".to_string(),
715            vendor_name:    "Somevendor".to_string(),
716            value:          "2".to_string()
717        });
718
719        let mut vendors: Vec<DictionaryVendor> = Vec::new();
720        vendors.push(DictionaryVendor {
721            name: "Somevendor".to_string(),
722            id:   10,
723        });
724
725        let expected_dict = Dictionary { attributes, values, vendors };
726        assert_eq!(dict, expected_dict)
727    }
728}