Example #1
0
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
}
Example #2
0
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
}