package asaModels import ( "fmt" "regexp" "strings" ) type ( PolicyMaps struct { Types []ASAPolicyMapType `json:"types"` Maps []ASAPolicyMap `json:"maps"` } ASAPolicyMapType struct { Mode string `json:"mode"` // Sample: inspect Type string `json:"type"` // Sample: dns Name string `json:"name"` // Sample: preset_dns_map Parameters []string `json:"parameters"` } ASAPolicyMap struct { Name string `json:"name"` Classes []ASAPolicyMapClass `json:"classes"` } ASAPolicyMapClass struct { Name string `json:"name"` Class string `json:"class,omitempty"` ClassType string `json:"type,omitempty"` Inspections []string `json:"inspections,omitempty"` SetCmds []string `json:"set_commands,omitempty"` } ) var ( regexPolicyMap = regexp.MustCompile(`policy-map (\S+)((?:.|\n)*)`) regexPolicyMapType = regexp.MustCompile(`policy-map type (\S+) (\S+) (\S+)((?:.|\n)*)`) regexPolicyTypeParams = regexp.MustCompile(`\n (.*)`) regexInspections = regexp.MustCompile(`inspect (.*)`) regexPMSetCmds = regexp.MustCompile(`set connection (.*)`) regexPMClass = regexp.MustCompile(`class (\S+)`) ) // Parse the raw output of a firewall in to a PolicyMaps structure func (p *PolicyMaps) Parse(s string) error { maps := separatePolicies(s) for _, cMap := range maps { m := regexPolicyMapType.FindStringSubmatch(cMap) if len(m) == 5 { // We have a policy-map type var pt ASAPolicyMapType if err := pt.Parse(m[3], m[2], m[1], m[4]); err != nil { return err } p.Types = append(p.Types, pt) } else { // Standard Policy Map spm := regexPolicyMap.FindStringSubmatch(cMap) if len(spm) > 0 { var pm ASAPolicyMap if err := pm.Parse(spm[1], spm[2]); err != nil { return err } p.Maps = append(p.Maps, pm) } } } return nil } // separatePolicies splits up the raw output from asa in to a slice of strings, each containing one policy map or type func separatePolicies(s string) []string { var maps []string // Split the input by newline split := strings.Split(s, "\n") var inner string i := 0 // Step over each line for _, line := range split { // Skip certain lines if line == "!" || line == "" || line == "show run policy-map" { continue } // If the line isn't indented (doesn't start with a space) then consider the previous group closed if line[0] != byte(' ') && i != 0 { // Append the inner group to the map list maps = append(maps, inner) inner = line i = 1 continue } if i == 0 { // First line of first group, set inner to it inner = line } else { // Append this line to the existing inner inner = fmt.Sprintf("%s\n%s", inner, line) } i++ } return maps } // Parse the raw output of a firewall in to a ASAPolicyMapType structure func (mt *ASAPolicyMapType) Parse(name, mode, Type, raw string) error { mt.Mode = mode mt.Name = name mt.Type = Type m := regexPolicyTypeParams.FindAllStringSubmatch(raw, -1) if len(m) > 0 { for _, line := range m { mt.Parameters = append(mt.Parameters, line[1]) } } return nil } // Parse the raw output of a firewall in to a ASAPolicyMap structure func (pm *ASAPolicyMap) Parse(name, s string) error { pm.Name = name classes := separateClasses(s) for _, class := range classes { var c ASAPolicyMapClass c.Parse(class) pm.Classes = append(pm.Classes, c) } return nil } func separateClasses(s string) []string { split := strings.Split(s, "\n") var classes []string var inner string for ln, line := range split { if line == "" { // Blank line continue } if regexPMClass.MatchString(line) { if len(inner) > 0 { // If there's existing lines in the inner string, append it to classes first classes = append(classes, strings.TrimSpace(inner)) } inner = line continue } inner = fmt.Sprintf("%s\n%s", inner, line) if ln == len(split)-1 && inner != "" { // last line reached, append inner to classes classes = append(classes, strings.TrimSpace(inner)) } } return classes } // Parse the raw output of a firewall in to a ASAPolicyMapClass structure func (c *ASAPolicyMapClass) Parse(s string) error { m := regexPMClass.FindStringSubmatch(s) if len(m) > 0 { c.Name = m[1] inspections := regexInspections.FindAllStringSubmatch(s, -1) if len(inspections) > 0 { for _, found := range inspections { c.Inspections = append(c.Inspections, strings.TrimSpace(found[1])) } } cmds := regexPMSetCmds.FindAllStringSubmatch(s, -1) if len(cmds) > 0 { for _, cmd := range cmds { c.SetCmds = append(c.SetCmds, strings.TrimSpace(cmd[1])) } } } return nil } type ( ServicePolicies struct { Global ServicePolicy `json:"global_service_policy"` Interface []ServicePolicy `json:"interface_policies"` } ServicePolicy struct { PolicyMap string `json:"policy_map"` Interface string `json:"interface,omitempty"` } ) func (sp *ServicePolicies) Parse(s string) error { m := regexp.MustCompile(`service-policy (\S+) global`).FindStringSubmatch(s) if len(m) > 0 { sp.Global.PolicyMap = m[1] return nil } m = regexp.MustCompile(`service-policy (\S+) interface (\S+)`).FindStringSubmatch(s) if len(m) > 0 { sp.Interface = append(sp.Interface, ServicePolicy{ PolicyMap: m[1], Interface: m[2], }) } return nil }