func parseLabeledValue(txt string) (label, value string, eatLen int) { feed := txt if idx := strings.IndexFunc(txt, oxm.IsSeparator); idx > 0 { feed = txt[:idx] } trail := []rune{} for _, c := range txt[len(feed):] { if oxm.IsSeparator(c) { trail = append(trail, c) } else { break } } kv := strings.SplitN(feed, "=", 2) if len(kv) > 1 { return kv[0], kv[1], len(feed) + len(string(trail)) } else { return kv[0], "", len(feed) + len(string(trail)) } }
func (self *flowRule) Parse(txt string) error { var actions []byte var delayed func() = nil phase := PHASE_MATCH for len(txt) > 0 { // in case value would include separator, don't use these value and just recalculate label, value, step := parseLabeledValue(txt) if label[0] == '@' && delayed != nil { delayed() delayed = nil } switch label { case "@meter": phase = PHASE_METER var meterId uint32 if err := parseInt(value, &meterId); err != nil { return err } var inst [8]byte binary.BigEndian.PutUint16(inst[:], OFPIT_METER) binary.BigEndian.PutUint16(inst[2:], 8) binary.BigEndian.PutUint32(inst[4:], meterId) self.Instructions = append(self.Instructions, inst[:]...) case "@apply", "@apply_actions": phase = PHASE_APPLY delayed = func() { var inst [8]byte binary.BigEndian.PutUint16(inst[:], OFPIT_APPLY_ACTIONS) binary.BigEndian.PutUint16(inst[2:], uint16(len(actions)+8)) self.Instructions = append(self.Instructions, inst[:]...) self.Instructions = append(self.Instructions, actions...) actions = actions[:0] } case "@clear", "@clear_actions": phase = PHASE_CLEAR var inst [8]byte binary.BigEndian.PutUint16(inst[:], OFPIT_CLEAR_ACTIONS) binary.BigEndian.PutUint16(inst[2:], 8) self.Instructions = append(self.Instructions, inst[:]...) case "@write", "@write_actions": phase = PHASE_WRITE delayed = func() { var inst [8]byte binary.BigEndian.PutUint16(inst[:], OFPIT_WRITE_ACTIONS) binary.BigEndian.PutUint16(inst[2:], uint16(len(actions)+8)) self.Instructions = append(self.Instructions, inst[:]...) self.Instructions = append(self.Instructions, actions...) actions = actions[:0] } case "@metadata", "@write_metadata": phase = PHASE_META var v, m uint64 vm := strings.SplitN(value, "/", 2) if err := parseInt(vm[0], &v); err != nil { return err } if len(vm) == 2 { if err := parseInt(vm[1], &m); err != nil { return err } } else { m = 0xFFFFFFFFFFFFFFFF } var inst [24]byte binary.BigEndian.PutUint16(inst[0:], OFPIT_WRITE_METADATA) binary.BigEndian.PutUint16(inst[2:], 24) binary.BigEndian.PutUint64(inst[8:], v) binary.BigEndian.PutUint64(inst[16:], m) self.Instructions = append(self.Instructions, inst[:]...) case "@goto", "@goto_table": phase = PHASE_GOTO var tableId uint8 if err := parseInt(value, &tableId); err != nil { return err } var inst [8]byte binary.BigEndian.PutUint16(inst[:], OFPIT_GOTO_TABLE) binary.BigEndian.PutUint16(inst[2:], uint16(len(inst))) inst[4] = tableId self.Instructions = append(self.Instructions, inst[:]...) default: switch phase { case PHASE_MATCH: switch label { case "table": var v uint8 if err := parseInt(value, &v); err != nil { return err } self.TableId = v case "priority", "idle_timeout", "hard_timeout": var v uint16 if err := parseInt(value, &v); err != nil { return err } switch label { case "idle_timeout": self.IdleTimeout = v case "hard_timeout": self.HardTimeout = v case "priority": self.Priority = v } case "cookie": var v, m uint64 vm := strings.SplitN(value, "/", 2) if err := parseInt(vm[0], &v); err != nil { return err } if len(vm) == 2 { if err := parseInt(vm[1], &m); err != nil { return err } } self.Cookie = v self.CookieMask = m case "out_port": var v uint32 if err := parseInt(value, &v); err != nil { return err } self.OutPort = v case "out_group", "group": var v uint32 if err := parseInt(value, &v); err != nil { return err } self.OutGroup = v default: if buf, n, err := oxm.ParseOne(txt); err != nil { return err } else { self.Match = append(self.Match, buf...) step = n } } case PHASE_APPLY, PHASE_WRITE: if buf, n, err := ParseAction(txt); err != nil { break } else { actions = append(actions, buf...) step = n } } } for i, c := range txt[step:] { if !oxm.IsSeparator(c) { step += i break } } txt = txt[step:] } if delayed != nil { delayed() } return nil }