package pkg import ( "errors" "regexp" "github.rackspace.com/gorax/tools/util" ) var ( LISTPFALLRE = regexp.MustCompile(`(?i)net\s+packet-filter\s+(\S+)\s+{((.|\n)*?)\n}`) ACTIONRE = regexp.MustCompile(`action\s+(\S+)`) ORDERNUMRE = regexp.MustCompile(`order\s+([0-9]+)`) RULERE = regexp.MustCompile(`(?i)rule\s+(.*)`) VLANRE = regexp.MustCompile(`vlan\s+(\S+)`) LOGGINGRE = regexp.MustCompile(`logging\s+(\S+)`) protore = regexp.MustCompile(`proto\s+(\S+)`) itemSubRe = regexp.MustCompile(`\((.*?)\)`) addressRe = regexp.MustCompile(`(src|dst)\s+(host|net)\s+(\S+)`) portRe = regexp.MustCompile(`(dst|src)\s+port\s+(\S+)`) andRe = regexp.MustCompile(`(?i)\s+and\s+`) ) type ( PacketFilter struct { Name string `json:"name"` Action string `json:"action"` Order int `json:"order"` Rule PFRule `json:"rule"` Vlan string `json:"vlan"` Logging string `json:"logging"` Proto []string `json:"proto"` } PFRule struct { Items []PFRuleItem `json:"items"` ACL ACL `json:"acl"` Valid bool `json:"valid"` Rule string `json:"rule"` Warnings []string `json:"warnings"` } PFRuleItem struct { Direction string `json:"direction"` Type string `json:"type"` Address string `json:"address"` Port string `json:"port"` } ACL struct { Src []PFRuleItem `json:"source"` Dst []PFRuleItem `json:"destination"` DstPort []PFRuleItem `json:"port_destination"` SrcPort []PFRuleItem `json:"Src_destination"` } ) func ParsePacketFilters(s string) ([]*PacketFilter, error) { var p []*PacketFilter for _, r := range LISTPFALLRE.FindAllStringSubmatch(s, -1) { p = append(p, &PacketFilter{ Name: r[1], Action: util.RegexpString(ACTIONRE, r[2]), Order: util.Atoi(util.RegexpString(ORDERNUMRE, r[2])), Rule: parsePFRule(util.RegexpString(RULERE, r[2])), Vlan: util.RegexpString(VLANRE, r[2]), Logging: util.RegexpString(LOGGINGRE, r[2]), Proto: func() (proto []string) { for _, m := range protore.FindAllStringSubmatch(r[2], -1) { proto = append(proto, m[1]) } return }(), }) } return p, nil } func parsePFRule(rule string) PFRule { var warn []string pf := PFRule{ Rule: rule, Items: func() (i []PFRuleItem) { for _, m := range itemSubRe.FindAllStringSubmatch(rule, -1) { rules, err := parsePFRuleItems(m[1]) i = append(i, rules...) if err != nil { warn = append(warn, err.Error()) } } return }(), Warnings: warn, Valid: len(warn) == 0, } if pf.Valid { pf.ACLGen() } return pf } func parsePFRuleItems(rule string) ([]PFRuleItem, error) { var i []PFRuleItem for _, m := range addressRe.FindAllStringSubmatch(rule, -1) { i = append(i, PFRuleItem{ Direction: m[1], Type: m[2], Address: m[3], }) } var p []PFRuleItem for _, m := range portRe.FindAllStringSubmatch(rule, -1) { p = append(p, PFRuleItem{ Direction: m[1], Type: "port", Port: m[2], }) } switch { case len(p) != 0 && len(i) != 0: i = append(i, p...) return i, errors.New("mix rule syntax, found ports and destinations in the same Items") case len(p) != 0: return p, ruleChecks(rule) default: return i, ruleChecks(rule) } } func ruleChecks(rule string) error { switch { case andRe.MatchString(rule): return errors.New("use of \"and\" in group") default: return nil } } func (pf *PFRule) ACLGen() { for _, i := range pf.Items { switch i.Type { case "port": switch { case i.Direction == "src": pf.ACL.SrcPort = append(pf.ACL.SrcPort, i) case i.Direction == "dst": pf.ACL.DstPort = append(pf.ACL.DstPort, i) default: log.Println("no match statement for port ", i.Type) } case "host", "net": switch { case i.Direction == "src": pf.ACL.Src = append(pf.ACL.Src, i) case i.Direction == "dst": pf.ACL.Dst = append(pf.ACL.Dst, i) default: log.Println("no match statement for host/net", i.Type) } } } }