Add code generator for string attribute

This commit is contained in:
moznion
2020-11-25 00:43:11 +09:00
parent 7e6831e282
commit 84943f10eb
8 changed files with 548 additions and 18 deletions

View File

@@ -16,4 +16,5 @@ tokio = { version = "0.3.4", features = ["full"] }
log = "0.4.11"
thiserror = "1.0"
regex = "1"
getopts = "0.2"
getopts = "0.2" # TODO for dev
Inflector = "0.11" # TODO for dev

3
Makefile Normal file
View File

@@ -0,0 +1,3 @@
gen:
cargo run --bin code_gen $(PWD)/dicts/dictionary.rfc2865 $(PWD)/src/rfc2865.rs
cargo fmt

138
dicts/dictionary.rfc2865 Normal file
View File

@@ -0,0 +1,138 @@
# -*- text -*-
# Copyright (C) 2011 The FreeRADIUS Server project and contributors
#
# Attributes and values defined in RFC 2865.
# http://www.ietf.org/rfc/rfc2865.txt
#
# $Id$
#
ATTRIBUTE User-Name 1 string
ATTRIBUTE User-Password 2 string encrypt=1
ATTRIBUTE CHAP-Password 3 octets
ATTRIBUTE NAS-IP-Address 4 ipaddr
ATTRIBUTE NAS-Port 5 integer
ATTRIBUTE Service-Type 6 integer
ATTRIBUTE Framed-Protocol 7 integer
ATTRIBUTE Framed-IP-Address 8 ipaddr
ATTRIBUTE Framed-IP-Netmask 9 ipaddr
ATTRIBUTE Framed-Routing 10 integer
ATTRIBUTE Filter-Id 11 string
ATTRIBUTE Framed-MTU 12 integer
ATTRIBUTE Framed-Compression 13 integer
ATTRIBUTE Login-IP-Host 14 ipaddr
ATTRIBUTE Login-Service 15 integer
ATTRIBUTE Login-TCP-Port 16 integer
# Attribute 17 is undefined
ATTRIBUTE Reply-Message 18 string
ATTRIBUTE Callback-Number 19 string
ATTRIBUTE Callback-Id 20 string
# Attribute 21 is undefined
ATTRIBUTE Framed-Route 22 string
ATTRIBUTE Framed-IPX-Network 23 ipaddr
ATTRIBUTE State 24 octets
ATTRIBUTE Class 25 octets
ATTRIBUTE Vendor-Specific 26 vsa
ATTRIBUTE Session-Timeout 27 integer
ATTRIBUTE Idle-Timeout 28 integer
ATTRIBUTE Termination-Action 29 integer
ATTRIBUTE Called-Station-Id 30 string
ATTRIBUTE Calling-Station-Id 31 string
ATTRIBUTE NAS-Identifier 32 string
ATTRIBUTE Proxy-State 33 octets
ATTRIBUTE Login-LAT-Service 34 string
ATTRIBUTE Login-LAT-Node 35 string
ATTRIBUTE Login-LAT-Group 36 octets
ATTRIBUTE Framed-AppleTalk-Link 37 integer
ATTRIBUTE Framed-AppleTalk-Network 38 integer
ATTRIBUTE Framed-AppleTalk-Zone 39 string
ATTRIBUTE CHAP-Challenge 60 octets
ATTRIBUTE NAS-Port-Type 61 integer
ATTRIBUTE Port-Limit 62 integer
ATTRIBUTE Login-LAT-Port 63 string
#
# Integer Translations
#
# Service types
VALUE Service-Type Login-User 1
VALUE Service-Type Framed-User 2
VALUE Service-Type Callback-Login-User 3
VALUE Service-Type Callback-Framed-User 4
VALUE Service-Type Outbound-User 5
VALUE Service-Type Administrative-User 6
VALUE Service-Type NAS-Prompt-User 7
VALUE Service-Type Authenticate-Only 8
VALUE Service-Type Callback-NAS-Prompt 9
VALUE Service-Type Call-Check 10
VALUE Service-Type Callback-Administrative 11
# Framed Protocols
VALUE Framed-Protocol PPP 1
VALUE Framed-Protocol SLIP 2
VALUE Framed-Protocol ARAP 3
VALUE Framed-Protocol Gandalf-SLML 4
VALUE Framed-Protocol Xylogics-IPX-SLIP 5
VALUE Framed-Protocol X.75-Synchronous 6
# Framed Routing Values
VALUE Framed-Routing None 0
VALUE Framed-Routing Broadcast 1
VALUE Framed-Routing Listen 2
VALUE Framed-Routing Broadcast-Listen 3
# Framed Compression Types
VALUE Framed-Compression None 0
VALUE Framed-Compression Van-Jacobson-TCP-IP 1
VALUE Framed-Compression IPX-Header-Compression 2
VALUE Framed-Compression Stac-LZS 3
# Login Services
VALUE Login-Service Telnet 0
VALUE Login-Service Rlogin 1
VALUE Login-Service TCP-Clear 2
VALUE Login-Service PortMaster 3
VALUE Login-Service LAT 4
VALUE Login-Service X25-PAD 5
VALUE Login-Service X25-T3POS 6
VALUE Login-Service TCP-Clear-Quiet 8
# Login-TCP-Port (see /etc/services for more examples)
VALUE Login-TCP-Port Telnet 23
VALUE Login-TCP-Port Rlogin 513
VALUE Login-TCP-Port Rsh 514
# Termination Options
VALUE Termination-Action Default 0
VALUE Termination-Action RADIUS-Request 1
# NAS Port Types
VALUE NAS-Port-Type Async 0
VALUE NAS-Port-Type Sync 1
VALUE NAS-Port-Type ISDN 2
VALUE NAS-Port-Type ISDN-V120 3
VALUE NAS-Port-Type ISDN-V110 4
VALUE NAS-Port-Type Virtual 5
VALUE NAS-Port-Type PIAFS 6
VALUE NAS-Port-Type HDLC-Clear-Channel 7
VALUE NAS-Port-Type X.25 8
VALUE NAS-Port-Type X.75 9
VALUE NAS-Port-Type G.3-Fax 10
VALUE NAS-Port-Type SDSL 11
VALUE NAS-Port-Type ADSL-CAP 12
VALUE NAS-Port-Type ADSL-DMT 13
VALUE NAS-Port-Type IDSL 14
VALUE NAS-Port-Type Ethernet 15
VALUE NAS-Port-Type xDSL 16
VALUE NAS-Port-Type Cable 17
VALUE NAS-Port-Type Wireless-Other 18
VALUE NAS-Port-Type Wireless-802.11 19

View File

@@ -1,12 +1,12 @@
use crate::attribute::Attribute;
pub type Type = u8;
pub type AVPType = u8;
pub const TYPE_INVALID: Type = 1;
pub const TYPE_INVALID: AVPType = 255;
#[derive(Debug, Clone, PartialEq)]
pub struct AVP {
typ: Type,
typ: AVPType,
attribute: Attribute,
}
@@ -43,10 +43,40 @@ impl Attributes {
Ok(Attributes(attrs))
}
pub fn add(&mut self, typ: Type, attribute: Attribute) {
pub(crate) fn add(&mut self, typ: AVPType, attribute: Attribute) {
self.0.push(AVP { typ, attribute })
}
pub(crate) fn del(&mut self, typ: AVPType) {
self.0 = self
.0
.iter()
.filter(|&avp| avp.typ != typ)
.cloned()
.collect();
}
pub(crate) fn lookup(&self, typ: AVPType) -> Option<&Attribute> {
self.0.iter().find_map(|avp| {
if avp.typ == typ {
return Some(&avp.attribute);
}
None
})
}
pub(crate) fn lookup_all(&self, typ: AVPType) -> Vec<&Attribute> {
self.0
.iter()
.filter_map(|avp| {
if avp.typ == typ {
Some(&avp.attribute);
}
None
})
.collect()
}
pub fn attributes_encoded_len(&self) -> Result<u16, String> {
let mut n: u16 = 0;
for attr in &self.0 {

View File

@@ -1,10 +1,12 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::BufRead;
use std::io::{BufRead, BufWriter, Write};
use std::path::Path;
use std::str::FromStr;
use std::{env, io, process};
use getopts::Options;
use inflector::Inflector;
use regex::Regex;
const ATTRIBUTE_KIND: &str = "ATTRIBUTE";
@@ -13,15 +15,38 @@ const VALUE_KIND: &str = "VALUE";
#[derive(Debug)]
struct RadiusAttribute {
name: String,
identifier: u16,
typ: String,
typ: u8,
value_type: RadiusAttributeValueType,
is_encrypt: bool,
}
#[derive(Debug)]
struct RadiusValue {
name: String,
identifier: u16,
typ: u16,
}
#[derive(Debug)]
enum RadiusAttributeValueType {
STRING,
OCTETS,
IPADDR,
INTEGER,
VSA,
}
impl FromStr for RadiusAttributeValueType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"STRING" => Ok(RadiusAttributeValueType::STRING),
"OCTETS" => Ok(RadiusAttributeValueType::OCTETS),
"IPADDR" => Ok(RadiusAttributeValueType::IPADDR),
"INTEGER" => Ok(RadiusAttributeValueType::INTEGER),
"VSA" => Ok(RadiusAttributeValueType::VSA),
_ => Err(()),
}
}
}
fn print_usage(program: &str, opts: &Options) {
@@ -47,15 +72,104 @@ fn main() {
.parse(&args[1..])
.unwrap_or_else(|f| panic!(f.to_string()));
let dict_file_path = matches.free[0].clone();
let dict_file_path = Path::new(&matches.free[0]);
if !dict_file_path.exists() {
panic!("no such dictionary file => {}", &matches.free[0]);
}
let rfc_code = dict_file_path.extension().unwrap().to_str().unwrap();
let struct_name = rfc_code.to_uppercase();
let (radius_attributes, radius_attribute_to_values) = parse_dict_file(dict_file_path).unwrap();
println!("{:?}", radius_attributes);
println!("{:?}", radius_attribute_to_values);
let mut buf_writer = BufWriter::new(File::create(&matches.free[1]).unwrap());
generate_header(&mut buf_writer, &struct_name);
generate_attributes_code(&mut buf_writer, &radius_attributes);
generate_footer(&mut buf_writer);
}
fn generate_header(w: &mut BufWriter<File>, struct_name: &String) {
let code = format!(
"// Code generated by machine generator; DO NOT EDIT.
use crate::attribute::Attribute;
use crate::attributes::AVPType;
use crate::packet::Packet;
pub struct {struct_name} {{
}}
impl {struct_name} {{
",
struct_name = struct_name
);
w.write_all(code.as_bytes()).unwrap();
}
fn generate_footer(w: &mut BufWriter<File>) {
w.write_all(b"}\n").unwrap();
}
fn generate_attributes_code(w: &mut BufWriter<File>, attrs: &[RadiusAttribute]) {
for attr in attrs {
generate_attribute_code(w, attr);
}
}
fn generate_attribute_code(w: &mut BufWriter<File>, attr: &RadiusAttribute) {
match attr.value_type {
RadiusAttributeValueType::STRING => generate_string_attribute_code(w, attr),
RadiusAttributeValueType::OCTETS => generate_octets_attribute_code(w, attr),
RadiusAttributeValueType::IPADDR => generate_ipaddr_attribute_code(w, attr),
RadiusAttributeValueType::INTEGER => generate_integer_attribute_code(w, attr),
RadiusAttributeValueType::VSA => generate_vsa_attribute_code(w, attr),
}
}
fn generate_string_attribute_code(w: &mut BufWriter<File>, attr: &RadiusAttribute) {
let attr_name = attr.name.clone();
let type_identifier = format!("{}_TYPE", attr_name.to_screaming_snake_case());
let type_calling = format!("Self::{}", type_identifier);
let code = format!(
"pub const {type_identifier}: AVPType = {type_value};
pub fn add_{method_identifier}(packet: &mut Packet, value: &str) {{
let attr = Attribute::from_string(value);
packet.add({type_calling}, &attr);
}}
pub fn delete_{method_identifier}(packet: &mut Packet) {{
packet.delete({type_calling});
}}
pub fn lookup_{method_identifier}(packet: &Packet) -> Option<&Attribute> {{
packet.lookup({type_calling})
}}
pub fn lookup_all_{method_identifier}(packet: &Packet) -> Vec<&Attribute> {{
packet.lookup_all({type_calling})
}}
",
method_identifier = attr_name.to_snake_case(),
type_identifier = type_identifier,
type_calling = type_calling,
type_value = attr.typ,
);
w.write_all(code.as_bytes()).unwrap();
}
fn generate_octets_attribute_code(w: &mut BufWriter<File>, attr: &RadiusAttribute) {}
fn generate_ipaddr_attribute_code(w: &mut BufWriter<File>, attr: &RadiusAttribute) {}
fn generate_integer_attribute_code(w: &mut BufWriter<File>, attr: &RadiusAttribute) {}
fn generate_vsa_attribute_code(w: &mut BufWriter<File>, attr: &RadiusAttribute) {}
type DictParsed = (Vec<RadiusAttribute>, HashMap<String, Vec<RadiusValue>>);
fn parse_dict_file(dict_file_path: String) -> Result<DictParsed, String> {
fn parse_dict_file(dict_file_path: &Path) -> Result<DictParsed, String> {
let line_filter_re = Regex::new(r"^(?:#.*|)$").unwrap();
let tabs_re = Regex::new(r"\t+").unwrap();
@@ -80,7 +194,14 @@ fn parse_dict_file(dict_file_path: String) -> Result<DictParsed, String> {
match kind {
ATTRIBUTE_KIND => {
let type_descriptions = items[3].split(' ').collect::<Vec<&str>>();
let typ = type_descriptions[0].to_string();
let typ = match RadiusAttributeValueType::from_str(type_descriptions[0]) {
Ok(t) => t,
Err(_) => {
return Err(
format!("invalid type has come => {}", type_descriptions[0]).to_owned()
);
}
};
let is_encrypt = if type_descriptions.len() >= 2 {
type_descriptions[1] == "encrypt=1" // FIXME: ad-hoc!!!
} else {
@@ -89,8 +210,8 @@ fn parse_dict_file(dict_file_path: String) -> Result<DictParsed, String> {
radius_attributes.push(RadiusAttribute {
name: items[1].to_string(),
identifier: items[2].parse().unwrap(),
typ,
typ: items[2].parse().unwrap(),
value_type: typ,
is_encrypt,
});
}
@@ -100,7 +221,7 @@ fn parse_dict_file(dict_file_path: String) -> Result<DictParsed, String> {
let radius_value = RadiusValue {
name,
identifier: items[3].parse().unwrap(),
typ: items[3].parse().unwrap(),
};
match radius_attribute_to_values.get_mut(&attribute_name) {

View File

@@ -1,3 +1,4 @@
extern crate inflector;
#[macro_use]
extern crate log;
@@ -8,5 +9,6 @@ pub mod code;
pub mod packet;
pub mod request;
pub mod request_handler;
pub mod rfc2865;
pub mod secret_provider;
pub mod server;

View File

@@ -2,7 +2,8 @@ use std::convert::TryInto;
use rand::Rng;
use crate::attributes::Attributes;
use crate::attribute::Attribute;
use crate::attributes::{AVPType, Attributes};
use crate::code::Code;
const MAX_PACKET_LENGTH: usize = 4096;
@@ -177,6 +178,22 @@ impl Packet {
_ => false,
}
}
pub fn add(&mut self, typ: AVPType, attr: &Attribute) {
self.attributes.add(typ, attr.clone());
}
pub fn delete(&mut self, typ: AVPType) {
self.attributes.del(typ);
}
pub fn lookup(&self, typ: AVPType) -> Option<&Attribute> {
self.attributes.lookup(typ)
}
pub fn lookup_all(&self, typ: AVPType) -> Vec<&Attribute> {
self.attributes.lookup_all(typ)
}
}
#[cfg(test)]

218
src/rfc2865.rs Normal file
View File

@@ -0,0 +1,218 @@
// Code generated by machine generator; DO NOT EDIT.
use crate::attribute::Attribute;
use crate::attributes::AVPType;
use crate::packet::Packet;
pub struct RFC2865 {}
impl RFC2865 {
pub const USER_NAME_TYPE: AVPType = 1;
pub fn add_user_name(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::USER_NAME_TYPE, &attr);
}
pub fn delete_user_name(packet: &mut Packet) {
packet.delete(Self::USER_NAME_TYPE);
}
pub fn lookup_user_name(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::USER_NAME_TYPE)
}
pub fn lookup_all_user_name(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::USER_NAME_TYPE)
}
pub const USER_PASSWORD_TYPE: AVPType = 2;
pub fn add_user_password(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::USER_PASSWORD_TYPE, &attr);
}
pub fn delete_user_password(packet: &mut Packet) {
packet.delete(Self::USER_PASSWORD_TYPE);
}
pub fn lookup_user_password(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::USER_PASSWORD_TYPE)
}
pub fn lookup_all_user_password(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::USER_PASSWORD_TYPE)
}
pub const FILTER_ID_TYPE: AVPType = 11;
pub fn add_filter_id(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::FILTER_ID_TYPE, &attr);
}
pub fn delete_filter_id(packet: &mut Packet) {
packet.delete(Self::FILTER_ID_TYPE);
}
pub fn lookup_filter_id(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::FILTER_ID_TYPE)
}
pub fn lookup_all_filter_id(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::FILTER_ID_TYPE)
}
pub const REPLY_MESSAGE_TYPE: AVPType = 18;
pub fn add_reply_message(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::REPLY_MESSAGE_TYPE, &attr);
}
pub fn delete_reply_message(packet: &mut Packet) {
packet.delete(Self::REPLY_MESSAGE_TYPE);
}
pub fn lookup_reply_message(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::REPLY_MESSAGE_TYPE)
}
pub fn lookup_all_reply_message(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::REPLY_MESSAGE_TYPE)
}
pub const CALLBACK_NUMBER_TYPE: AVPType = 19;
pub fn add_callback_number(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::CALLBACK_NUMBER_TYPE, &attr);
}
pub fn delete_callback_number(packet: &mut Packet) {
packet.delete(Self::CALLBACK_NUMBER_TYPE);
}
pub fn lookup_callback_number(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::CALLBACK_NUMBER_TYPE)
}
pub fn lookup_all_callback_number(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::CALLBACK_NUMBER_TYPE)
}
pub const CALLBACK_ID_TYPE: AVPType = 20;
pub fn add_callback_id(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::CALLBACK_ID_TYPE, &attr);
}
pub fn delete_callback_id(packet: &mut Packet) {
packet.delete(Self::CALLBACK_ID_TYPE);
}
pub fn lookup_callback_id(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::CALLBACK_ID_TYPE)
}
pub fn lookup_all_callback_id(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::CALLBACK_ID_TYPE)
}
pub const FRAMED_ROUTE_TYPE: AVPType = 22;
pub fn add_framed_route(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::FRAMED_ROUTE_TYPE, &attr);
}
pub fn delete_framed_route(packet: &mut Packet) {
packet.delete(Self::FRAMED_ROUTE_TYPE);
}
pub fn lookup_framed_route(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::FRAMED_ROUTE_TYPE)
}
pub fn lookup_all_framed_route(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::FRAMED_ROUTE_TYPE)
}
pub const CALLED_STATION_ID_TYPE: AVPType = 30;
pub fn add_called_station_id(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::CALLED_STATION_ID_TYPE, &attr);
}
pub fn delete_called_station_id(packet: &mut Packet) {
packet.delete(Self::CALLED_STATION_ID_TYPE);
}
pub fn lookup_called_station_id(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::CALLED_STATION_ID_TYPE)
}
pub fn lookup_all_called_station_id(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::CALLED_STATION_ID_TYPE)
}
pub const CALLING_STATION_ID_TYPE: AVPType = 31;
pub fn add_calling_station_id(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::CALLING_STATION_ID_TYPE, &attr);
}
pub fn delete_calling_station_id(packet: &mut Packet) {
packet.delete(Self::CALLING_STATION_ID_TYPE);
}
pub fn lookup_calling_station_id(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::CALLING_STATION_ID_TYPE)
}
pub fn lookup_all_calling_station_id(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::CALLING_STATION_ID_TYPE)
}
pub const NAS_IDENTIFIER_TYPE: AVPType = 32;
pub fn add_nas_identifier(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::NAS_IDENTIFIER_TYPE, &attr);
}
pub fn delete_nas_identifier(packet: &mut Packet) {
packet.delete(Self::NAS_IDENTIFIER_TYPE);
}
pub fn lookup_nas_identifier(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::NAS_IDENTIFIER_TYPE)
}
pub fn lookup_all_nas_identifier(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::NAS_IDENTIFIER_TYPE)
}
pub const LOGIN_LAT_SERVICE_TYPE: AVPType = 34;
pub fn add_login_lat_service(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::LOGIN_LAT_SERVICE_TYPE, &attr);
}
pub fn delete_login_lat_service(packet: &mut Packet) {
packet.delete(Self::LOGIN_LAT_SERVICE_TYPE);
}
pub fn lookup_login_lat_service(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::LOGIN_LAT_SERVICE_TYPE)
}
pub fn lookup_all_login_lat_service(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::LOGIN_LAT_SERVICE_TYPE)
}
pub const LOGIN_LAT_NODE_TYPE: AVPType = 35;
pub fn add_login_lat_node(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::LOGIN_LAT_NODE_TYPE, &attr);
}
pub fn delete_login_lat_node(packet: &mut Packet) {
packet.delete(Self::LOGIN_LAT_NODE_TYPE);
}
pub fn lookup_login_lat_node(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::LOGIN_LAT_NODE_TYPE)
}
pub fn lookup_all_login_lat_node(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::LOGIN_LAT_NODE_TYPE)
}
pub const FRAMED_APPLE_TALK_ZONE_TYPE: AVPType = 39;
pub fn add_framed_apple_talk_zone(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::FRAMED_APPLE_TALK_ZONE_TYPE, &attr);
}
pub fn delete_framed_apple_talk_zone(packet: &mut Packet) {
packet.delete(Self::FRAMED_APPLE_TALK_ZONE_TYPE);
}
pub fn lookup_framed_apple_talk_zone(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::FRAMED_APPLE_TALK_ZONE_TYPE)
}
pub fn lookup_all_framed_apple_talk_zone(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::FRAMED_APPLE_TALK_ZONE_TYPE)
}
pub const LOGIN_LAT_PORT_TYPE: AVPType = 63;
pub fn add_login_lat_port(packet: &mut Packet, value: &str) {
let attr = Attribute::from_string(value);
packet.add(Self::LOGIN_LAT_PORT_TYPE, &attr);
}
pub fn delete_login_lat_port(packet: &mut Packet) {
packet.delete(Self::LOGIN_LAT_PORT_TYPE);
}
pub fn lookup_login_lat_port(packet: &Packet) -> Option<&Attribute> {
packet.lookup(Self::LOGIN_LAT_PORT_TYPE)
}
pub fn lookup_all_login_lat_port(packet: &Packet) -> Vec<&Attribute> {
packet.lookup_all(Self::LOGIN_LAT_PORT_TYPE)
}
}