func ParseAction(txt string) (buf []byte, eatLen int, err error) { generic := func(atype uint16) { buf = make([]byte, 8) binary.BigEndian.PutUint16(buf, atype) } label, value, eatLen := parseLabeledValue(txt) switch label { case "copy_ttl_out": generic(OFPAT_COPY_TTL_OUT) case "copy_ttl_in": generic(OFPAT_COPY_TTL_IN) case "dec_mpls_ttl": generic(OFPAT_DEC_MPLS_TTL) case "pop_vlan": generic(OFPAT_POP_VLAN) case "dec_nw_ttl": generic(OFPAT_DEC_NW_TTL) case "pop_pbb": generic(OFPAT_POP_PBB) case "output": vs := strings.SplitN(value, ":", 2) var port uint32 switch vs[0] { case "max": port = OFPP_MAX case "in_port": port = OFPP_IN_PORT case "table": port = OFPP_TABLE case "normal": port = OFPP_NORMAL case "flood": port = OFPP_FLOOD case "all": port = OFPP_ALL case "controller": port = OFPP_CONTROLLER case "local": port = OFPP_LOCAL case "any": port = OFPP_ANY default: if err = parseInt(vs[0], &port); err != nil { return } } maxLen := uint16(OFPCML_NO_BUFFER) if len(vs) > 1 { if err = parseInt(vs[1], &maxLen); err != nil { return } } buf = make([]byte, 16) binary.BigEndian.PutUint16(buf, OFPAT_OUTPUT) binary.BigEndian.PutUint32(buf[4:], port) binary.BigEndian.PutUint16(buf[8:], maxLen) case "set_mpls_ttl", "set_nw_ttl": var v uint8 if err = parseInt(value, &v); err != nil { return } switch label { case "set_mpls_ttl": generic(OFPAT_SET_MPLS_TTL) case "set_nw_ttl": generic(OFPAT_SET_NW_TTL) } buf[4] = v case "push_vlan", "push_mpls", "pop_mpls", "push_pbb": var v uint16 if err = parseInt(value, &v); err != nil { return } switch label { case "push_vlan": generic(OFPAT_PUSH_VLAN) case "push_mpls": generic(OFPAT_PUSH_MPLS) case "pop_mpls": generic(OFPAT_POP_MPLS) case "push_pbb": generic(OFPAT_PUSH_PBB) } binary.BigEndian.PutUint16(buf[4:], v) case "group", "set_queue": var v uint32 if err = parseInt(value, &v); err != nil { return } switch label { case "group": generic(OFPAT_GROUP) case "set_queue": generic(OFPAT_SET_QUEUE) } binary.BigEndian.PutUint32(buf[4:], v) default: // set-field if strings.HasPrefix(label, "set_") { setLen := len("set_") if p, n, e := oxm.ParseOne(txt[setLen:]); e != nil { err = e } else { buf = make([]byte, align8(4+len(p))) binary.BigEndian.PutUint16(buf, OFPAT_SET_FIELD) copy(buf[4:], p) eatLen = setLen + n } } else { for _, handler := range actionStringers { if buf, eatLen, err = handler.ToAction(txt); err == nil { return } } } } if len(buf) == 0 { err = fmt.Errorf("unparsed") } else { binary.BigEndian.PutUint16(buf[2:], uint16(len(buf))) } return }
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 }