public static DeviceOracle createDeviceOracle(
      String macStr,
      Long vlan,
      String entityClassName,
      String ipStr,
      String dpidStr,
      Short switchPortNumber)
      throws BigDBException {
    if (macStr == null) {
      throw new BigDBException("Mac Address cannot be null.");
    }
    Short vlanShort = vlan == null ? null : Short.valueOf(vlan.shortValue());
    Integer port = switchPortNumber == null ? null : Integer.valueOf(switchPortNumber);
    Integer ipv4 = ipStr == null ? null : IPv4.toIPv4Address(ipStr);
    Long dpid = dpidStr == null ? null : HexString.toLong(dpidStr);
    IEntityClass entityClass =
        getDeviceService().getEntityClassifier().getEntityClassByName(entityClassName);
    if (entityClass == null) {
      throw new BigDBException(
          "Invalid entity class name, config the " + "entity class first: " + entityClassName);
    }
    Entity en = new Entity(HexString.toLong(macStr), vlanShort, ipv4, dpid, port, null);

    String deviceId = IndexedEntity.getKeyString(entityClassName, en, entityClass.getKeyFields());
    return new DeviceOracle(deviceId, macStr, vlanShort, entityClassName, ipStr, dpidStr, port);
  }
 @Override
 public String toString() {
   switch (operation) {
     case LINK_REMOVED:
     case LINK_UPDATED:
       return "LDUpdate [operation="
           + operation
           + ", src="
           + HexString.toHexString(src)
           + ", srcPort="
           + srcPort
           + ", dst="
           + HexString.toHexString(dst)
           + ", dstPort="
           + dstPort
           + ", type="
           + type
           + "]";
     case PORT_DOWN:
     case PORT_UP:
       return "LDUpdate [operation="
           + operation
           + ", src="
           + HexString.toHexString(src)
           + ", srcPort="
           + srcPort
           + "]";
     case SWITCH_REMOVED:
     case SWITCH_UPDATED:
       return "LDUpdate [operation=" + operation + ", src=" + HexString.toHexString(src) + "]";
     default:
       return "LDUpdate: Unknown update.";
   }
 }
 public String toString() {
   return ";fid="
       + fieldId
       + ";ofst="
       + offset
       + ";len="
       + length
       + ";val="
       + HexString.toHex(value)
       + ";mask="
       + HexString.toHex(mask);
 }
  public String getFullHexMask() {
    String fullHexMaskString = "";
    if (mask == null) {
      fullHexMaskString += HexString.Zero((length - 1) / 8 + 1);
    } else {
      if (mask.length >= OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE) {
        fullHexMaskString += HexString.toHex(mask, 0, OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE);
      } else {
        fullHexMaskString += HexString.toHex(mask);
        // fullHexMaskString += HexString.Zero(OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE - mask.length);
      }
    }

    return fullHexMaskString;
  }
 public void _controllerShowSwitches(CommandInterpreter ci) {
   Set<Long> sids = switches.keySet();
   StringBuffer s = new StringBuffer();
   int size = sids.size();
   if (size == 0) {
     ci.print("switches: empty");
     return;
   }
   Iterator<Long> iter = sids.iterator();
   s.append("Total: " + size + " switches\n");
   while (iter.hasNext()) {
     Long sid = iter.next();
     Date date = switches.get(sid).getConnectedDate();
     String switchInstanceName = ((SwitchHandler) switches.get(sid)).getInstanceName();
     s.append(
         switchInstanceName
             + "/"
             + HexString.toHexString(sid)
             + " connected since "
             + date.toString()
             + "\n");
   }
   ci.print(s.toString());
   return;
 }
  public String getFullHexValue() {
    String fullHexValueString = "";
    if (value == null) {
      fullHexValueString += HexString.Zero((length - 1) / 8 + 1);
    } else {
      if (value.length >= OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE) {
        fullHexValueString += HexString.toHex(value, 0, OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE);
      } else {
        fullHexValueString += HexString.toHex(value);
        // fullHexValueString += HexString.Zero(OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE -
        // value.length);
      }
    }

    return fullHexValueString;
  }
 @Override
 public String toString() {
   StringBuffer sb = new StringBuffer();
   for (long n : links.keySet()) {
     sb.append(
         "[" + HexString.toHexString(n) + ": cost=" + costs.get(n) + ", " + links.get(n) + "]");
   }
   return sb.toString();
 }
 public DeviceOracle(
     String deviceId,
     long mac,
     Short vlan,
     String entityClassName,
     Integer ipAddress,
     Long switchDPID,
     Integer switchPortNumber)
     throws BigDBException {
   if (deviceId == null) {
     throw new BigDBException("Device ID cannot be null.");
   }
   String ip = ipAddress == null ? null : IPv4.fromIPv4Address(ipAddress);
   String dpid = switchDPID == null ? null : HexString.toHexString(switchDPID, 8);
   String macStr = HexString.toHexString(mac, 6);
   this.deviceId = deviceId;
   this.mac = macStr;
   this.vlan = vlan;
   this.entityClassName = entityClassName;
   this.ipAddress = ip;
   this.switchDPID = dpid;
   this.switchPortNumber = switchPortNumber;
 }
Exemple #9
0
  /**
   * Output a dpctl-styled string, i.e., only list the elements that are not wildcarded
   *
   * <p>A match-everything OFMatch outputs "OFMatch[]"
   *
   * @return "OFMatch[dl_src:00:20:01:11:22:33,nw_src:192.168.0.0/24,tp_dst:80]"
   */
  @Override
  public String toString() {

    String str = "";

    // l1
    if ((wildcards & OFPFW_IN_PORT) == 0) str += "," + STR_IN_PORT + "=" + U16.f(this.inputPort);

    // l2
    if ((wildcards & OFPFW_DL_DST) == 0)
      str += "," + STR_DL_DST + "=" + HexString.toHexString(this.dataLayerDestination);
    if ((wildcards & OFPFW_DL_SRC) == 0)
      str += "," + STR_DL_SRC + "=" + HexString.toHexString(this.dataLayerSource);
    if ((wildcards & OFPFW_DL_TYPE) == 0)
      str += "," + STR_DL_TYPE + "=0x" + Integer.toHexString(U16.f(this.dataLayerType));
    if ((wildcards & OFPFW_DL_VLAN) == 0)
      str += "," + STR_DL_VLAN + "=" + U16.f(this.dataLayerVirtualLan);
    if ((wildcards & OFPFW_DL_VLAN_PCP) == 0)
      str += "," + STR_DL_VLAN_PCP + "=" + U8.f(this.dataLayerVirtualLanPriorityCodePoint);

    // l3
    if (getNetworkDestinationMaskLen() > 0)
      str +=
          "," + STR_NW_DST + "=" + cidrToString(networkDestination, getNetworkDestinationMaskLen());
    if (getNetworkSourceMaskLen() > 0)
      str += "," + STR_NW_SRC + "=" + cidrToString(networkSource, getNetworkSourceMaskLen());
    if ((wildcards & OFPFW_NW_PROTO) == 0) str += "," + STR_NW_PROTO + "=" + this.networkProtocol;
    if ((wildcards & OFPFW_NW_TOS) == 0) str += "," + STR_NW_TOS + "=" + this.networkTypeOfService;

    // l4
    if ((wildcards & OFPFW_TP_DST) == 0) str += "," + STR_TP_DST + "=" + this.transportDestination;
    if ((wildcards & OFPFW_TP_SRC) == 0) str += "," + STR_TP_SRC + "=" + this.transportSource;
    if ((str.length() > 0) && (str.charAt(0) == ','))
      str = str.substring(1); // trim the leading ","
    // done
    return "OFMatch[" + str + "]";
  }
 @Override
 @JsonIgnore
 public void setFeaturesReply(OFFeaturesReply featuresReply) {
   synchronized (portLock) {
     if (stringId == null) {
       /* ports are updated via port status message, so we
        * only fill in ports on initial connection.
        */
       for (OFPhysicalPort port : featuresReply.getPorts()) {
         setPort(port);
       }
     }
     this.datapathId = featuresReply.getDatapathId();
     this.capabilities = featuresReply.getCapabilities();
     this.buffers = featuresReply.getBuffers();
     this.actions = featuresReply.getActions();
     this.tables = featuresReply.getTables();
     this.stringId = HexString.toHexString(this.datapathId);
   }
 }
Exemple #11
0
 /*
  * (non-Javadoc)
  *
  * @see org.flowvisor.api.FVUserAPI#getDeviceInfo()
  */
 @Override
 public Map<String, String> getDeviceInfo(String dpidStr) throws DPIDNotFound {
   Map<String, String> map = new HashMap<String, String>();
   long dpid = HexString.toLong(dpidStr);
   FVClassifier fvClassifier = null;
   for (FVEventHandler handler : FlowVisor.getInstance().getHandlersCopy()) {
     if (handler instanceof FVClassifier) {
       OFFeaturesReply featuresReply = ((FVClassifier) handler).getSwitchInfo();
       if (featuresReply != null && featuresReply.getDatapathId() == dpid) {
         fvClassifier = (FVClassifier) handler;
         break;
       }
     }
   }
   if (fvClassifier == null)
     throw new DPIDNotFound("dpid does not exist: " + dpidStr + " ::" + String.valueOf(dpid));
   OFFeaturesReply config = fvClassifier.getSwitchInfo();
   map.put("dpid", FlowSpaceUtil.dpidToString(dpid));
   if (config != null) {
     map.put("nPorts", String.valueOf(config.getPorts().size()));
     String portList = "";
     String portNames = "";
     int p;
     for (Iterator<OFPhysicalPort> it = config.getPorts().iterator(); it.hasNext(); ) {
       OFPhysicalPort port = it.next();
       p = U16.f(port.getPortNumber());
       portList += p;
       portNames += port.getName() + "(" + p + ")";
       if (it.hasNext()) {
         portList += ",";
         portNames += ",";
       }
     }
     map.put("portList", portList);
     map.put("portNames", portNames);
   } else {
     FVLog.log(LogLevel.WARN, null, "null config for: " + dpidStr);
   }
   map.put("remote", String.valueOf(fvClassifier.getConnectionName()));
   return map;
 }
 /**
  * API to return a String value formed wtih NodeID and PortID The portID is a 16-bit field, so
  * mask it as an integer to get full positive value
  *
  * @return
  */
 public String toKeyString() {
   return (HexString.toHexString(nodeId) + "|" + (portId & 0xffff));
 }
 public String toString() {
   return "[id=" + HexString.toHexString(nodeId) + ", port=" + new Short(portId) + "]";
 }
  public String toBytesString() {
    String string =
        HexString.toHex(fieldId)
            + HexString.toHex(offset)
            + HexString.toHex(length)
            + HexString.ByteZeroEnd(2);

    if (value == null) {
      string += HexString.ByteZeroEnd(OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE);
    } else {
      if (value.length > OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE) {
        string += HexString.toHex(value, 0, OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE);
        string += HexString.ZeroEnd(0);
      } else {
        // string += HexString.ZeroEnd(0);
        string += HexString.toHex(value);
        // string += HexString.ZeroEnd(0);
        string += HexString.ByteZeroEnd(OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE - value.length);
      }
    }

    if (mask == null) {
      string += HexString.ByteZeroEnd(OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE);
    } else {
      if (mask.length > OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE) {
        string += HexString.toHex(mask, 0, OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE);
        string += HexString.ZeroEnd(0);
      } else {
        // string += HexString.ZeroEnd(0);
        string += HexString.toHex(mask);
        // string += HexString.ZeroEnd(0);
        string += HexString.ByteZeroEnd(OFGlobal.OFP_MAX_FIELD_LENGTH_IN_BYTE - mask.length);
      }
    }

    return string;
  }
Exemple #15
0
 /**
  * Set this OFMatch's parameters based on a comma-separated key=value pair dpctl-style string,
  * e.g., from the output of OFMatch.toString() <br>
  *
  * <p>Supported keys/values include <br>
  *
  * <p>
  *
  * <TABLE border=1>
  * <TR>
  * <TD>KEY(s)
  * <TD>VALUE
  * </TR>
  * <TR>
  * <TD>"in_port","input_port"
  * <TD>integer
  * </TR>
  * <TR>
  * <TD>"dl_src","eth_src", "dl_dst","eth_dst"
  * <TD>hex-string
  * </TR>
  * <TR>
  * <TD>"dl_type", "dl_vlan", "dl_vlan_pcp"
  * <TD>integer
  * </TR>
  * <TR>
  * <TD>"nw_src", "nw_dst", "ip_src", "ip_dst"
  * <TD>CIDR-style netmask
  * </TR>
  * <TR>
  * <TD>"tp_src","tp_dst"
  * <TD>integer (max 64k)
  * </TR>
  * </TABLE>
  *
  * <p>The CIDR-style netmasks assume 32 netmask if none given, so: "128.8.128.118/32" is the same
  * as "128.8.128.118"
  *
  * @param match a key=value comma separated string, e.g.
  *     "in_port=5,ip_dst=192.168.0.0/16,tp_src=80"
  * @throws IllegalArgumentException on unexpected key or value
  */
 public void fromString(String match) throws IllegalArgumentException {
   if (match.equals("")
       || match.equalsIgnoreCase("any")
       || match.equalsIgnoreCase("all")
       || match.equals("[]")) match = "OFMatch[]";
   String[] tokens = match.split("[\\[,\\]]");
   String[] values;
   int initArg = 0;
   if (tokens[0].equals("OFMatch")) initArg = 1;
   this.wildcards = OFPFW_ALL;
   int i;
   for (i = initArg; i < tokens.length; i++) {
     values = tokens[i].split("=");
     if (values.length != 2)
       throw new IllegalArgumentException(
           "Token " + tokens[i] + " does not have form 'key=value' parsing " + match);
     values[0] = values[0].toLowerCase(); // try to make this case insens
     if (values[0].equals(STR_IN_PORT) || values[0].equals("input_port")) {
       this.inputPort = U16.t(Integer.valueOf(values[1]));
       this.wildcards &= ~OFPFW_IN_PORT;
     } else if (values[0].equals(STR_DL_DST) || values[0].equals("eth_dst")) {
       this.dataLayerDestination = HexString.fromHexString(values[1]);
       this.wildcards &= ~OFPFW_DL_DST;
     } else if (values[0].equals(STR_DL_SRC) || values[0].equals("eth_src")) {
       this.dataLayerSource = HexString.fromHexString(values[1]);
       this.wildcards &= ~OFPFW_DL_SRC;
     } else if (values[0].equals(STR_DL_TYPE) || values[0].equals("eth_type")) {
       if (values[1].startsWith("0x"))
         this.dataLayerType = U16.t(Integer.valueOf(values[1].replaceFirst("0x", ""), 16));
       else this.dataLayerType = U16.t(Integer.valueOf(values[1]));
       this.wildcards &= ~OFPFW_DL_TYPE;
     } else if (values[0].equals(STR_DL_VLAN)) {
       if (values[1].startsWith("0x"))
         this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1].replaceFirst("0x", ""), 16));
       else this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1]));
       this.wildcards &= ~OFPFW_DL_VLAN;
     } else if (values[0].equals(STR_DL_VLAN_PCP)) {
       if (values[1].startsWith("0x"))
         this.dataLayerVirtualLanPriorityCodePoint =
             U8.t(Short.valueOf(values[1].replaceFirst("0x", ""), 16));
       else this.dataLayerVirtualLanPriorityCodePoint = U8.t(Short.valueOf(values[1]));
       this.wildcards &= ~OFPFW_DL_VLAN_PCP;
     } else if (values[0].equals(STR_NW_DST) || values[0].equals("ip_dst"))
       setFromCIDR(values[1], STR_NW_DST);
     else if (values[0].equals(STR_NW_SRC) || values[0].equals("ip_src"))
       setFromCIDR(values[1], STR_NW_SRC);
     else if (values[0].equals(STR_NW_PROTO)) {
       this.networkProtocol = U8.t(Short.valueOf(values[1]));
       this.wildcards &= ~OFPFW_NW_PROTO;
     } else if (values[0].equals(STR_NW_TOS)) {
       this.networkTypeOfService = U8.t(Short.valueOf(values[1]));
       this.wildcards &= ~OFPFW_NW_TOS;
     } else if (values[0].equals(STR_TP_DST)) {
       this.transportDestination = U16.t(Integer.valueOf(values[1]));
       this.wildcards &= ~OFPFW_TP_DST;
     } else if (values[0].equals(STR_TP_SRC)) {
       this.transportSource = U16.t(Integer.valueOf(values[1]));
       this.wildcards &= ~OFPFW_TP_SRC;
     } else throw new IllegalArgumentException("unknown token " + tokens[i] + " parsing " + match);
   }
 }
  @Test
  public void testStaticFlowPush() throws IOException {
    StaticFlowEntryPusher staticFlowEntryPusher = new StaticFlowEntryPusher();
    IStorageSourceService storage = createStorageWithFlowEntries();
    long dpid = HexString.toLong(TestSwitch1DPID);

    // Create a Switch and attach a switch
    IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class);
    Capture<OFMessage> writeCapture = new Capture<OFMessage>(CaptureType.ALL);
    Capture<ListenerContext> contextCapture = new Capture<ListenerContext>(CaptureType.ALL);
    Capture<List<OFMessage>> writeCaptureList = new Capture<List<OFMessage>>(CaptureType.ALL);

    // OFMessageSafeOutStream mockOutStream = createNiceMock(OFMessageSafeOutStream.class);
    mockSwitch.write(capture(writeCapture), capture(contextCapture));
    expectLastCall().anyTimes();
    mockSwitch.write(capture(writeCaptureList), capture(contextCapture));
    expectLastCall().anyTimes();
    mockSwitch.flush();
    expectLastCall().anyTimes();

    staticFlowEntryPusher.setStorageSource(storage);

    ModuleContext fmc = new ModuleContext();

    MockControllerProvider mockControllerProvider = getMockControllerProvider();
    Map<Long, IOFSwitch> switchMap = new HashMap<Long, IOFSwitch>();
    switchMap.put(dpid, mockSwitch);
    // NO ! expect(mockControllerProvider.getSwitches()).andReturn(switchMap).anyTimes();
    mockControllerProvider.setSwitches(switchMap);
    staticFlowEntryPusher.setControllerProvider(mockControllerProvider);
    RestApiServer restApi = new RestApiServer();
    try {
      restApi.init(fmc);
    } catch (ModuleException e) {
      e.printStackTrace();
    }
    staticFlowEntryPusher.restApi = restApi;
    staticFlowEntryPusher.startUp(null); // again, to hack unittest

    // verify that flowpusher read all three entries from storage
    assertEquals(TotalTestRules, staticFlowEntryPusher.countEntries());

    // if someone calls mockSwitch.getOutputStream(), return mockOutStream instead
    // expect(mockSwitch.getOutputStream()).andReturn(mockOutStream).anyTimes();

    // if someone calls getId(), return this dpid instead
    expect(mockSwitch.getId()).andReturn(dpid).anyTimes();
    expect(mockSwitch.getStringId()).andReturn(TestSwitch1DPID).anyTimes();
    replay(mockSwitch);

    // hook the static pusher up to the fake switch
    staticFlowEntryPusher.addedSwitch(mockSwitch);

    verify(mockSwitch);

    // Verify that the switch has gotten some flow_mods
    assertEquals(true, writeCapture.hasCaptured());
    assertEquals(TotalTestRules, writeCapture.getValues().size());

    // Order assumes how things are stored in hash bucket;
    // should be fixed because OFMessage.hashCode() is deterministic
    OFFlowMod firstFlowMod = (OFFlowMod) writeCapture.getValues().get(2);
    verifyFlowMod(firstFlowMod, FlowMod1);
    OFFlowMod secondFlowMod = (OFFlowMod) writeCapture.getValues().get(1);
    verifyFlowMod(secondFlowMod, FlowMod2);
    OFFlowMod thirdFlowMod = (OFFlowMod) writeCapture.getValues().get(0);
    verifyFlowMod(thirdFlowMod, FlowMod3);

    writeCapture.reset();
    contextCapture.reset();

    // delete two rules and verify they've been removed
    // this should invoke staticFlowPusher.rowsDeleted()
    storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule1");
    storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule2");

    assertEquals(1, staticFlowEntryPusher.countEntries());
    assertEquals(2, writeCapture.getValues().size());

    OFFlowMod firstDelete = (OFFlowMod) writeCapture.getValues().get(0);
    FlowMod1.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
    verifyFlowMod(firstDelete, FlowMod1);

    OFFlowMod secondDelete = (OFFlowMod) writeCapture.getValues().get(1);
    FlowMod2.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
    verifyFlowMod(secondDelete, FlowMod2);

    // add rules back to make sure that staticFlowPusher.rowsInserted() works
    writeCapture.reset();
    FlowMod2.setCommand(OFFlowMod.OFPFC_ADD);
    storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2);
    assertEquals(2, staticFlowEntryPusher.countEntries());
    assertEquals(1, writeCaptureList.getValues().size());
    List<OFMessage> outList = (List<OFMessage>) writeCaptureList.getValues().get(0);
    assertEquals(1, outList.size());
    OFFlowMod firstAdd = (OFFlowMod) outList.get(0);
    verifyFlowMod(firstAdd, FlowMod2);
    writeCapture.reset();
    contextCapture.reset();
    writeCaptureList.reset();

    // now try an overwriting update, calling staticFlowPusher.rowUpdated()
    TestRule3.put(COLUMN_DL_VLAN, 333);
    storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3);
    assertEquals(2, staticFlowEntryPusher.countEntries());
    assertEquals(1, writeCaptureList.getValues().size());

    outList = (List<OFMessage>) writeCaptureList.getValues().get(0);
    assertEquals(2, outList.size());
    OFFlowMod removeFlowMod = (OFFlowMod) outList.get(0);
    FlowMod3.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
    verifyFlowMod(removeFlowMod, FlowMod3);
    FlowMod3.setCommand(OFFlowMod.OFPFC_ADD);
    FlowMod3.getMatch().fromString("dl_dst=00:20:30:40:50:60,dl_vlan=333");
    OFFlowMod updateFlowMod = (OFFlowMod) outList.get(1);
    verifyFlowMod(updateFlowMod, FlowMod3);
    writeCaptureList.reset();

    // now try an action modifying update, calling staticFlowPusher.rowUpdated()
    TestRule3.put(COLUMN_ACTIONS, "output=controller,strip-vlan"); // added strip-vlan
    storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3);
    assertEquals(2, staticFlowEntryPusher.countEntries());
    assertEquals(1, writeCaptureList.getValues().size());

    outList = (List<OFMessage>) writeCaptureList.getValues().get(0);
    assertEquals(1, outList.size());
    OFFlowMod modifyFlowMod = (OFFlowMod) outList.get(0);
    FlowMod3.setCommand(OFFlowMod.OFPFC_MODIFY_STRICT);
    List<OFAction> modifiedActions = FlowMod3.getActions();
    modifiedActions.add(
        new OFActionStripVirtualLan()); // add the new action to what we should expect
    FlowMod3.setActions(modifiedActions);
    FlowMod3.setLengthU(OFFlowMod.MINIMUM_LENGTH + 16); // accommodate the addition of new actions
    verifyFlowMod(modifyFlowMod, FlowMod3);
  }
 public String toBytesString() {
   return super.toBytesString()
       + HexString.toHex(fieldPosition)
       + HexString.ByteZeroEnd(2)
       + HexString.toHex(fieldLength);
 }
  public String toBytesString() {
    String string = super.toString();
    string +=
        HexString.toHex(command)
            + HexString.toHex(groupType)
            + HexString.toHex(actionNum)
            + HexString.ByteZeroEnd(1)
            + HexString.toHex(groupId)
            + HexString.toHex(counterId)
            + HexString.ByteZeroEnd(4);

    if (actionList == null) {
      string +=
          HexString.ByteZeroEnd(OFGlobal.OFP_MAX_ACTION_NUMBER_PER_GROUP * OFAction.MAXIMAL_LENGTH);
    } else {
      OFAction action;

      if (actionNum > actionList.size()) {
        throw new RuntimeException(
            "actionNum " + actionNum + " > actionList.size()" + actionList.size());
      }

      int i;
      for (i = 0; i < actionNum && i < OFGlobal.OFP_MAX_ACTION_NUMBER_PER_GROUP; i++) {
        action = actionList.get(i);
        if (action == null) {
          string += HexString.ByteZeroEnd(OFAction.MAXIMAL_LENGTH);
        } else {
          string += action.toBytesString();
          if (action.getLength() < OFAction.MAXIMAL_LENGTH) {
            string += HexString.ByteZeroEnd(OFAction.MAXIMAL_LENGTH - action.getLength());
          }
        }
      }
      if (i < OFGlobal.OFP_MAX_ACTION_NUMBER_PER_GROUP) {
        string +=
            HexString.ByteZeroEnd(
                (OFGlobal.OFP_MAX_ACTION_NUMBER_PER_GROUP - i) * OFAction.MAXIMAL_LENGTH);
      }
    }

    return string;
  }