mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 10:18:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			582 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			582 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2023 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package knftables
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| func parseInt(numbersOnly string) *int {
 | |
| 	i64, _ := strconv.ParseInt(numbersOnly, 10, 64)
 | |
| 	i := int(i64)
 | |
| 	return &i
 | |
| }
 | |
| 
 | |
| func parseUint(numbersOnly string) *uint64 {
 | |
| 	ui64, _ := strconv.ParseUint(numbersOnly, 10, 64)
 | |
| 	return &ui64
 | |
| }
 | |
| 
 | |
| // getComment parses a match for the commentGroup regexp (below). To distinguish between empty comment and no comment,
 | |
| // we capture comment with double quotes.
 | |
| func getComment(commentGroup string) *string {
 | |
| 	if commentGroup == "" {
 | |
| 		return nil
 | |
| 	}
 | |
| 	noQuotes := strings.Trim(commentGroup, "\"")
 | |
| 	return &noQuotes
 | |
| }
 | |
| 
 | |
| var commentGroup = `(".*")`
 | |
| var noSpaceGroup = `([^ ]*)`
 | |
| var numberGroup = `([0-9]*)`
 | |
| 
 | |
| // Object implementation for Table
 | |
| func (table *Table) validate(verb verb) error {
 | |
| 	switch verb {
 | |
| 	case addVerb, createVerb, flushVerb:
 | |
| 		if table.Handle != nil {
 | |
| 			return fmt.Errorf("cannot specify Handle in %s operation", verb)
 | |
| 		}
 | |
| 	case deleteVerb:
 | |
| 		// Handle can be nil or non-nil
 | |
| 	default:
 | |
| 		return fmt.Errorf("%s is not implemented for tables", verb)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (table *Table) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
 | |
| 	// Special case for delete-by-handle
 | |
| 	if verb == deleteVerb && table.Handle != nil {
 | |
| 		fmt.Fprintf(writer, "delete table %s handle %d", ctx.family, *table.Handle)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// All other cases refer to the table by name
 | |
| 	fmt.Fprintf(writer, "%s table %s %s", verb, ctx.family, ctx.table)
 | |
| 	if verb == addVerb || verb == createVerb {
 | |
| 		if table.Comment != nil && !ctx.noObjectComments {
 | |
| 			fmt.Fprintf(writer, " { comment %q ; }", *table.Comment)
 | |
| 		}
 | |
| 	}
 | |
| 	fmt.Fprintf(writer, "\n")
 | |
| }
 | |
| 
 | |
| var tableRegexp = regexp.MustCompile(fmt.Sprintf(
 | |
| 	`(?:{ comment %s ; })?`, commentGroup))
 | |
| 
 | |
| func (table *Table) parse(line string) error {
 | |
| 	match := tableRegexp.FindStringSubmatch(line)
 | |
| 	if match == nil {
 | |
| 		return fmt.Errorf("failed parsing table add command")
 | |
| 	}
 | |
| 	table.Comment = getComment(match[1])
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Object implementation for Chain
 | |
| func (chain *Chain) validate(verb verb) error {
 | |
| 	if chain.Hook == nil {
 | |
| 		if chain.Type != nil || chain.Priority != nil {
 | |
| 			return fmt.Errorf("regular chain %q must not specify Type or Priority", chain.Name)
 | |
| 		}
 | |
| 		if chain.Device != nil {
 | |
| 			return fmt.Errorf("regular chain %q must not specify Device", chain.Name)
 | |
| 		}
 | |
| 	} else {
 | |
| 		if chain.Type == nil || chain.Priority == nil {
 | |
| 			return fmt.Errorf("base chain %q must specify Type and Priority", chain.Name)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch verb {
 | |
| 	case addVerb, createVerb, flushVerb:
 | |
| 		if chain.Name == "" {
 | |
| 			return fmt.Errorf("no name specified for chain")
 | |
| 		}
 | |
| 		if chain.Handle != nil {
 | |
| 			return fmt.Errorf("cannot specify Handle in %s operation", verb)
 | |
| 		}
 | |
| 	case deleteVerb:
 | |
| 		if chain.Name == "" && chain.Handle == nil {
 | |
| 			return fmt.Errorf("must specify either name or handle")
 | |
| 		}
 | |
| 	default:
 | |
| 		return fmt.Errorf("%s is not implemented for chains", verb)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (chain *Chain) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
 | |
| 	// Special case for delete-by-handle
 | |
| 	if verb == deleteVerb && chain.Handle != nil {
 | |
| 		fmt.Fprintf(writer, "delete chain %s %s handle %d", ctx.family, ctx.table, *chain.Handle)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "%s chain %s %s %s", verb, ctx.family, ctx.table, chain.Name)
 | |
| 	if verb == addVerb || verb == createVerb {
 | |
| 		if chain.Type != nil || (chain.Comment != nil && !ctx.noObjectComments) {
 | |
| 			fmt.Fprintf(writer, " {")
 | |
| 
 | |
| 			if chain.Type != nil {
 | |
| 				fmt.Fprintf(writer, " type %s hook %s", *chain.Type, *chain.Hook)
 | |
| 				if chain.Device != nil {
 | |
| 					fmt.Fprintf(writer, " device %q", *chain.Device)
 | |
| 				}
 | |
| 
 | |
| 				// Parse the priority to a number if we can, because older
 | |
| 				// versions of nft don't accept certain named priorities
 | |
| 				// in all contexts (eg, "dstnat" priority in the "output"
 | |
| 				// hook).
 | |
| 				if priority, err := ParsePriority(ctx.family, string(*chain.Priority)); err == nil {
 | |
| 					fmt.Fprintf(writer, " priority %d ;", priority)
 | |
| 				} else {
 | |
| 					fmt.Fprintf(writer, " priority %s ;", *chain.Priority)
 | |
| 				}
 | |
| 			}
 | |
| 			if chain.Comment != nil && !ctx.noObjectComments {
 | |
| 				fmt.Fprintf(writer, " comment %q ;", *chain.Comment)
 | |
| 			}
 | |
| 
 | |
| 			fmt.Fprintf(writer, " }")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "\n")
 | |
| }
 | |
| 
 | |
| // groups in []: [1]%s(?: {(?: type [2]%s hook [3]%s(?: device "[4]%s")(?: priority [5]%s ;))(?: comment [6]%s ;) })
 | |
| var chainRegexp = regexp.MustCompile(fmt.Sprintf(
 | |
| 	`%s(?: {(?: type %s hook %s(?: device "%s")?(?: priority %s ;))?(?: comment %s ;)? })?`,
 | |
| 	noSpaceGroup, noSpaceGroup, noSpaceGroup, noSpaceGroup, noSpaceGroup, commentGroup))
 | |
| 
 | |
| func (chain *Chain) parse(line string) error {
 | |
| 	match := chainRegexp.FindStringSubmatch(line)
 | |
| 	if match == nil {
 | |
| 		return fmt.Errorf("failed parsing chain add command")
 | |
| 	}
 | |
| 	chain.Name = match[1]
 | |
| 	chain.Comment = getComment(match[6])
 | |
| 	if match[2] != "" {
 | |
| 		chain.Type = (*BaseChainType)(&match[2])
 | |
| 	}
 | |
| 	if match[3] != "" {
 | |
| 		chain.Hook = (*BaseChainHook)(&match[3])
 | |
| 	}
 | |
| 	if match[4] != "" {
 | |
| 		chain.Device = &match[4]
 | |
| 	}
 | |
| 	if match[5] != "" {
 | |
| 		chain.Priority = (*BaseChainPriority)(&match[5])
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Object implementation for Rule
 | |
| func (rule *Rule) validate(verb verb) error {
 | |
| 	if rule.Chain == "" {
 | |
| 		return fmt.Errorf("no chain name specified for rule")
 | |
| 	}
 | |
| 
 | |
| 	if rule.Index != nil && rule.Handle != nil {
 | |
| 		return fmt.Errorf("cannot specify both Index and Handle")
 | |
| 	}
 | |
| 
 | |
| 	switch verb {
 | |
| 	case addVerb, insertVerb:
 | |
| 		if rule.Rule == "" {
 | |
| 			return fmt.Errorf("no rule specified")
 | |
| 		}
 | |
| 	case replaceVerb:
 | |
| 		if rule.Rule == "" {
 | |
| 			return fmt.Errorf("no rule specified")
 | |
| 		}
 | |
| 		if rule.Handle == nil {
 | |
| 			return fmt.Errorf("must specify Handle with %s", verb)
 | |
| 		}
 | |
| 	case deleteVerb:
 | |
| 		if rule.Handle == nil {
 | |
| 			return fmt.Errorf("must specify Handle with %s", verb)
 | |
| 		}
 | |
| 	default:
 | |
| 		return fmt.Errorf("%s is not implemented for rules", verb)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (rule *Rule) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
 | |
| 	fmt.Fprintf(writer, "%s rule %s %s %s", verb, ctx.family, ctx.table, rule.Chain)
 | |
| 	if rule.Index != nil {
 | |
| 		fmt.Fprintf(writer, " index %d", *rule.Index)
 | |
| 	} else if rule.Handle != nil {
 | |
| 		fmt.Fprintf(writer, " handle %d", *rule.Handle)
 | |
| 	}
 | |
| 
 | |
| 	switch verb {
 | |
| 	case addVerb, insertVerb, replaceVerb:
 | |
| 		fmt.Fprintf(writer, " %s", rule.Rule)
 | |
| 
 | |
| 		if rule.Comment != nil {
 | |
| 			fmt.Fprintf(writer, " comment %q", *rule.Comment)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "\n")
 | |
| }
 | |
| 
 | |
| // groups in []: [1]%s(?: index [2]%s)?(?: handle [3]%s)? [4]([^"]*)(?: comment [5]%s)?$
 | |
| var ruleRegexp = regexp.MustCompile(fmt.Sprintf(
 | |
| 	`%s(?: index %s)?(?: handle %s)? ([^"]*)(?: comment %s)?$`,
 | |
| 	noSpaceGroup, numberGroup, numberGroup, commentGroup))
 | |
| 
 | |
| func (rule *Rule) parse(line string) error {
 | |
| 	match := ruleRegexp.FindStringSubmatch(line)
 | |
| 	if match == nil {
 | |
| 		return fmt.Errorf("failed parsing rule add command")
 | |
| 	}
 | |
| 	rule.Chain = match[1]
 | |
| 	rule.Rule = match[4]
 | |
| 	rule.Comment = getComment(match[5])
 | |
| 	if match[2] != "" {
 | |
| 		rule.Index = parseInt(match[2])
 | |
| 	}
 | |
| 	if match[3] != "" {
 | |
| 		rule.Handle = parseInt(match[3])
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Object implementation for Set
 | |
| func (set *Set) validate(verb verb) error {
 | |
| 	switch verb {
 | |
| 	case addVerb, createVerb:
 | |
| 		if (set.Type == "" && set.TypeOf == "") || (set.Type != "" && set.TypeOf != "") {
 | |
| 			return fmt.Errorf("set must specify either Type or TypeOf")
 | |
| 		}
 | |
| 		if set.Handle != nil {
 | |
| 			return fmt.Errorf("cannot specify Handle in %s operation", verb)
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	case flushVerb:
 | |
| 		if set.Name == "" {
 | |
| 			return fmt.Errorf("no name specified for set")
 | |
| 		}
 | |
| 	case deleteVerb:
 | |
| 		if set.Name == "" && set.Handle == nil {
 | |
| 			return fmt.Errorf("must specify either name or handle")
 | |
| 		}
 | |
| 	default:
 | |
| 		return fmt.Errorf("%s is not implemented for sets", verb)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (set *Set) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
 | |
| 	// Special case for delete-by-handle
 | |
| 	if verb == deleteVerb && set.Handle != nil {
 | |
| 		fmt.Fprintf(writer, "delete set %s %s handle %d", ctx.family, ctx.table, *set.Handle)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "%s set %s %s %s", verb, ctx.family, ctx.table, set.Name)
 | |
| 	if verb == addVerb || verb == createVerb {
 | |
| 		fmt.Fprintf(writer, " {")
 | |
| 
 | |
| 		if set.Type != "" {
 | |
| 			fmt.Fprintf(writer, " type %s ;", set.Type)
 | |
| 		} else {
 | |
| 			fmt.Fprintf(writer, " typeof %s ;", set.TypeOf)
 | |
| 		}
 | |
| 
 | |
| 		if len(set.Flags) != 0 {
 | |
| 			fmt.Fprintf(writer, " flags ")
 | |
| 			for i := range set.Flags {
 | |
| 				if i > 0 {
 | |
| 					fmt.Fprintf(writer, ",")
 | |
| 				}
 | |
| 				fmt.Fprintf(writer, "%s", set.Flags[i])
 | |
| 			}
 | |
| 			fmt.Fprintf(writer, " ;")
 | |
| 		}
 | |
| 
 | |
| 		if set.Timeout != nil {
 | |
| 			fmt.Fprintf(writer, " timeout %ds ;", int64(set.Timeout.Seconds()))
 | |
| 		}
 | |
| 		if set.GCInterval != nil {
 | |
| 			fmt.Fprintf(writer, " gc-interval %ds ;", int64(set.GCInterval.Seconds()))
 | |
| 		}
 | |
| 		if set.Size != nil {
 | |
| 			fmt.Fprintf(writer, " size %d ;", *set.Size)
 | |
| 		}
 | |
| 		if set.Policy != nil {
 | |
| 			fmt.Fprintf(writer, " policy %s ;", *set.Policy)
 | |
| 		}
 | |
| 		if set.AutoMerge != nil && *set.AutoMerge {
 | |
| 			fmt.Fprintf(writer, " auto-merge ;")
 | |
| 		}
 | |
| 
 | |
| 		if set.Comment != nil && !ctx.noObjectComments {
 | |
| 			fmt.Fprintf(writer, " comment %q ;", *set.Comment)
 | |
| 		}
 | |
| 
 | |
| 		fmt.Fprintf(writer, " }")
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "\n")
 | |
| }
 | |
| 
 | |
| func (set *Set) parse(line string) error {
 | |
| 	match := setRegexp.FindStringSubmatch(line)
 | |
| 	if match == nil {
 | |
| 		return fmt.Errorf("failed parsing set add command")
 | |
| 	}
 | |
| 	set.Name, set.Type, set.TypeOf, set.Flags, set.Timeout, set.GCInterval,
 | |
| 		set.Size, set.Policy, set.Comment, set.AutoMerge = parseMapAndSetProps(match)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Object implementation for Map
 | |
| func (mapObj *Map) validate(verb verb) error {
 | |
| 	switch verb {
 | |
| 	case addVerb, createVerb:
 | |
| 		if (mapObj.Type == "" && mapObj.TypeOf == "") || (mapObj.Type != "" && mapObj.TypeOf != "") {
 | |
| 			return fmt.Errorf("map must specify either Type or TypeOf")
 | |
| 		}
 | |
| 		if mapObj.Handle != nil {
 | |
| 			return fmt.Errorf("cannot specify Handle in %s operation", verb)
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	case flushVerb:
 | |
| 		if mapObj.Name == "" {
 | |
| 			return fmt.Errorf("no name specified for map")
 | |
| 		}
 | |
| 	case deleteVerb:
 | |
| 		if mapObj.Name == "" && mapObj.Handle == nil {
 | |
| 			return fmt.Errorf("must specify either name or handle")
 | |
| 		}
 | |
| 	default:
 | |
| 		return fmt.Errorf("%s is not implemented for maps", verb)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (mapObj *Map) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
 | |
| 	// Special case for delete-by-handle
 | |
| 	if verb == deleteVerb && mapObj.Handle != nil {
 | |
| 		fmt.Fprintf(writer, "delete map %s %s handle %d", ctx.family, ctx.table, *mapObj.Handle)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "%s map %s %s %s", verb, ctx.family, ctx.table, mapObj.Name)
 | |
| 	if verb == addVerb || verb == createVerb {
 | |
| 		fmt.Fprintf(writer, " {")
 | |
| 
 | |
| 		if mapObj.Type != "" {
 | |
| 			fmt.Fprintf(writer, " type %s ;", mapObj.Type)
 | |
| 		} else {
 | |
| 			fmt.Fprintf(writer, " typeof %s ;", mapObj.TypeOf)
 | |
| 		}
 | |
| 
 | |
| 		if len(mapObj.Flags) != 0 {
 | |
| 			fmt.Fprintf(writer, " flags ")
 | |
| 			for i := range mapObj.Flags {
 | |
| 				if i > 0 {
 | |
| 					fmt.Fprintf(writer, ",")
 | |
| 				}
 | |
| 				fmt.Fprintf(writer, "%s", mapObj.Flags[i])
 | |
| 			}
 | |
| 			fmt.Fprintf(writer, " ;")
 | |
| 		}
 | |
| 
 | |
| 		if mapObj.Timeout != nil {
 | |
| 			fmt.Fprintf(writer, " timeout %ds ;", int64(mapObj.Timeout.Seconds()))
 | |
| 		}
 | |
| 		if mapObj.GCInterval != nil {
 | |
| 			fmt.Fprintf(writer, " gc-interval %ds ;", int64(mapObj.GCInterval.Seconds()))
 | |
| 		}
 | |
| 		if mapObj.Size != nil {
 | |
| 			fmt.Fprintf(writer, " size %d ;", *mapObj.Size)
 | |
| 		}
 | |
| 		if mapObj.Policy != nil {
 | |
| 			fmt.Fprintf(writer, " policy %s ;", *mapObj.Policy)
 | |
| 		}
 | |
| 
 | |
| 		if mapObj.Comment != nil && !ctx.noObjectComments {
 | |
| 			fmt.Fprintf(writer, " comment %q ;", *mapObj.Comment)
 | |
| 		}
 | |
| 
 | |
| 		fmt.Fprintf(writer, " }")
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "\n")
 | |
| }
 | |
| 
 | |
| func (mapObj *Map) parse(line string) error {
 | |
| 	match := mapRegexp.FindStringSubmatch(line)
 | |
| 	if match == nil {
 | |
| 		return fmt.Errorf("failed parsing map add command")
 | |
| 	}
 | |
| 	mapObj.Name, mapObj.Type, mapObj.TypeOf, mapObj.Flags, mapObj.Timeout, mapObj.GCInterval,
 | |
| 		mapObj.Size, mapObj.Policy, mapObj.Comment, _ = parseMapAndSetProps(match)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| var autoMergeProp = `( auto-merge ;)?`
 | |
| 
 | |
| // groups in []:  [1]%s {(?: [2](type|typeof) [3]([^;]*)) ;(?: flags [4]([^;]*) ;)?(?: timeout [5]%ss ;)?(?: gc-interval [6]%ss ;)?(?: size [7]%s ;)?(?: policy [8]%s ;)?[9]%s(?: comment [10]%s ;)? }
 | |
| var mapOrSet = `%s {(?: (type|typeof) ([^;]*)) ;(?: flags ([^;]*) ;)?(?: timeout %ss ;)?(?: gc-interval %ss ;)?(?: size %s ;)?(?: policy %s ;)?%s(?: comment %s ;)? }`
 | |
| var mapRegexp = regexp.MustCompile(fmt.Sprintf(mapOrSet, noSpaceGroup, numberGroup, numberGroup, noSpaceGroup, noSpaceGroup, "", commentGroup))
 | |
| var setRegexp = regexp.MustCompile(fmt.Sprintf(mapOrSet, noSpaceGroup, numberGroup, numberGroup, noSpaceGroup, noSpaceGroup, autoMergeProp, commentGroup))
 | |
| 
 | |
| func parseMapAndSetProps(match []string) (name string, typeProp string, typeOf string, flags []SetFlag,
 | |
| 	timeout *time.Duration, gcInterval *time.Duration, size *uint64, policy *SetPolicy, comment *string, autoMerge *bool) {
 | |
| 	name = match[1]
 | |
| 	// set and map have different number of match groups, but comment is always the last
 | |
| 	comment = getComment(match[len(match)-1])
 | |
| 	if match[2] == "type" {
 | |
| 		typeProp = match[3]
 | |
| 	} else {
 | |
| 		typeOf = match[3]
 | |
| 	}
 | |
| 	if match[4] != "" {
 | |
| 		flags = parseSetFlags(match[4])
 | |
| 	}
 | |
| 	if match[5] != "" {
 | |
| 		timeoutObj, _ := time.ParseDuration(match[5] + "s")
 | |
| 		timeout = &timeoutObj
 | |
| 	}
 | |
| 	if match[6] != "" {
 | |
| 		gcIntervalObj, _ := time.ParseDuration(match[6] + "s")
 | |
| 		gcInterval = &gcIntervalObj
 | |
| 	}
 | |
| 	if match[7] != "" {
 | |
| 		size = parseUint(match[7])
 | |
| 	}
 | |
| 	if match[8] != "" {
 | |
| 		policy = (*SetPolicy)(&match[8])
 | |
| 	}
 | |
| 	if len(match) > 10 {
 | |
| 		// set
 | |
| 		if match[9] != "" {
 | |
| 			autoMergeObj := true
 | |
| 			autoMerge = &autoMergeObj
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func parseSetFlags(s string) []SetFlag {
 | |
| 	var res []SetFlag
 | |
| 	for _, flag := range strings.Split(s, ",") {
 | |
| 		res = append(res, SetFlag(flag))
 | |
| 	}
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| // Object implementation for Element
 | |
| func (element *Element) validate(verb verb) error {
 | |
| 	if element.Map == "" && element.Set == "" {
 | |
| 		return fmt.Errorf("no set/map name specified for element")
 | |
| 	} else if element.Set != "" && element.Map != "" {
 | |
| 		return fmt.Errorf("element specifies both a set name and a map name")
 | |
| 	}
 | |
| 
 | |
| 	if len(element.Key) == 0 {
 | |
| 		return fmt.Errorf("no key specified for element")
 | |
| 	}
 | |
| 	if element.Set != "" && len(element.Value) != 0 {
 | |
| 		return fmt.Errorf("map value specified for set element")
 | |
| 	}
 | |
| 
 | |
| 	switch verb {
 | |
| 	case addVerb, createVerb:
 | |
| 		if element.Map != "" && len(element.Value) == 0 {
 | |
| 			return fmt.Errorf("no map value specified for map element")
 | |
| 		}
 | |
| 	case deleteVerb:
 | |
| 	default:
 | |
| 		return fmt.Errorf("%s is not implemented for elements", verb)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (element *Element) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
 | |
| 	name := element.Set
 | |
| 	if name == "" {
 | |
| 		name = element.Map
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, "%s element %s %s %s { %s", verb, ctx.family, ctx.table, name,
 | |
| 		strings.Join(element.Key, " . "))
 | |
| 
 | |
| 	if verb == addVerb || verb == createVerb {
 | |
| 		if element.Comment != nil {
 | |
| 			fmt.Fprintf(writer, " comment %q", *element.Comment)
 | |
| 		}
 | |
| 
 | |
| 		if len(element.Value) != 0 {
 | |
| 			fmt.Fprintf(writer, " : %s", strings.Join(element.Value, " . "))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fmt.Fprintf(writer, " }\n")
 | |
| }
 | |
| 
 | |
| // groups in []: [1]%s { [2]([^:"]*)(?: comment [3]%s)? : [4](.*) }
 | |
| var mapElementRegexp = regexp.MustCompile(fmt.Sprintf(
 | |
| 	`%s { ([^"]*)(?: comment %s)? : (.*) }`, noSpaceGroup, commentGroup))
 | |
| 
 | |
| // groups in []: [1]%s { [2]([^:"]*)(?: comment [3]%s)? }
 | |
| var setElementRegexp = regexp.MustCompile(fmt.Sprintf(
 | |
| 	`%s { ([^"]*)(?: comment %s)? }`, noSpaceGroup, commentGroup))
 | |
| 
 | |
| func (element *Element) parse(line string) error {
 | |
| 	// try to match map element first, since it has more groups, and if it matches, then we can be sure
 | |
| 	// this is map element.
 | |
| 	match := mapElementRegexp.FindStringSubmatch(line)
 | |
| 	if match == nil {
 | |
| 		match = setElementRegexp.FindStringSubmatch(line)
 | |
| 		if match == nil {
 | |
| 			return fmt.Errorf("failed parsing element add command")
 | |
| 		}
 | |
| 	}
 | |
| 	element.Comment = getComment(match[3])
 | |
| 	mapOrSetName := match[1]
 | |
| 	element.Key = append(element.Key, strings.Split(match[2], " . ")...)
 | |
| 	if len(match) == 5 {
 | |
| 		// map regex matched
 | |
| 		element.Map = mapOrSetName
 | |
| 		element.Value = append(element.Value, strings.Split(match[4], " . ")...)
 | |
| 	} else {
 | |
| 		element.Set = mapOrSetName
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | 
