public bool DecodeBACnet(byte[] buffer, int totalMessageLength) { int offset = 0; try { // this whole section http://www.bacnetwiki.com/wiki/index.php?title=BACnet_Virtual_Link_Control ADR sadr = null; if (buffer[offset] != BACnetEnums.BACNET_BVLC_TYPE_BIP) { // todo3, panic log _apm.MessageProtocolError("m0013 - Not a BACnet/IP message"); return(false); } // we could receive an original broadcast, a unicast, a forwarded here... // BVLC Function Types. See http://www.bacnetwiki.com/wiki/index.php?title=BVLC_Function switch ((BACnetEnums.BACNET_BVLC_FUNCTION)buffer[offset + 1]) { case BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_FORWARDED_NPDU: npdu_offset = offset + 10; break; case BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_UNICAST_NPDU: case BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_BROADCAST_NPDU: // all these OK npdu_offset = offset + 4; break; default: BACnetLibraryCL.Panic("m0012 nosuch bvlc function"); break; } // Investigate the NPDU // http://www.bacnetwiki.com/wiki/index.php?title=NPDU if (buffer[npdu_offset] != BACnetEnums.BACNET_PROTOCOL_VERSION) { // we have a major problem, microprotocol version has changed. http://www.bacnetwiki.com/wiki/index.php?title=BACnet_Virtual_Link_Control BACnetLibraryCL.Panic("m0011 BVLC microprotocol problem"); return(false); } // expecting reply? if ((buffer[npdu_offset + 1] & 0x04) == 0x04) { npdu.expectingReply = true; } // destination address present? // http://www.bacnetwiki.com/wiki/index.php?title=NPCI if ((buffer[npdu_offset + 1] & 0x20) == 0x20) { //da_present = true; dadr = new ADR(); // dnet, dadr and hop count present int dAddrOffset = npdu_offset + 2; dadr.Decode(buffer, ref dAddrOffset); if (dadr.MACaddress.length == 0) { npdu.isBroadcast = true; // broadcast, but check the DNET if (dadr.networkNumber != 0xffff) { throw new Exception("m0010 - Broadcast according to DLEN, but DNET not 0xffff"); // todo, this means directed broadcast, need to deal with this still } } if (dadr.MACaddress.length != 1 && dadr.MACaddress.length != 6 && dadr.MACaddress.length != 0) { // panic throw new Exception("m0009 - Unexpected DADR len"); } // todo, pick up variable length destination address } // Source address present? // http://www.bacnetwiki.com/wiki/index.php?title=NPCI if ((buffer[npdu_offset + 1] & 0x08) == 0x08) { //sa_present = true; sadr = new ADR(); int sa_offset = npdu_offset + 2; // however, if there is a destination address, move the sa_offset up appropriate amount if (dadr != null) { sa_offset = npdu_offset + 2 + 3 + (int)dadr.MACaddress.length; } // means SADR, SNET present sadr.Decode(buffer, ref sa_offset); // SA exists (included MAC and so this means the received IP address needs to be the fromBIP } else { // at this point, if SADR not discovered within the NPDU, then the SADR MAC address is the Ethernet/IP fromaddress // and the device can (must) be considered 'direcly connected' if (directlyConnectedIPEndPointOfDevice != null) { // and even though the device is directly connected, because we are a router, we have an allocated network number to provide // the network number is available outside this class, and will be filled in by the calling function if it is relevant. srcDevice.adr = new ADR(0, directlyConnectedIPEndPointOfDevice); } else { throw new Exception("m0063 - No From-Address can be determined"); } } if (dadr != null) { if (sadr != null) { hopcount = (uint)(buffer[npdu_offset + 2 + 2 + 1 + dadr.MACaddress.length + 2 + 1 + sadr.MACaddress.length]); } else { hopcount = (uint)(buffer[npdu_offset + 2 + 2 + 1 + dadr.MACaddress.length]); } // true broadcast, but check the hopcount if (hopcount == 0) { // out of hops, should never happen to us, so sound a warning // todo, remove this for a functioning systems System.Windows.Forms.MessageBox.Show("m0008 Hopcount of 0 detected"); return(false); } } // finished resolving sadr, and dadr. Now populate our devices as required if (sadr != null) { if (srcDevice.adr != null) { // means this adr was partially populated elsewhere. srcDevice.adr.networkNumber = sadr.networkNumber; srcDevice.adr.MACaddress = sadr.MACaddress; } else { srcDevice.adr = sadr; } } if ((buffer[npdu_offset + 1] & 0x80) == 0x80) // todo magic number { // NSDU contains Network Layer Message // http://www.bacnetwiki.com/wiki/index.php?title=Network_Layer_Protocol_Control_Information apdu_present = false; npdu.isNPDUmessage = true; // calculate offset to the NSDU nsdu_offset = npdu_offset + 2; if (sadr != null) { nsdu_offset += 2 + 1 + (int)sadr.MACaddress.length; } if (dadr != null) { nsdu_offset += 2 + 1 + (int)dadr.MACaddress.length + 1; } npdu.function = (BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE)buffer[nsdu_offset]; switch (npdu.function) { case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: // there may be a list of network numbers after the header if ((length - nsdu_offset >= 3)) { numberList = new List <uint>(); } for (int i = 0; i < (length - nsdu_offset - 1) / 2; i++) { int tref = nsdu_offset + 1 + i * 2; numberList.Add(BACnetLibraryCL.ExtractUint16(buffer, ref tref)); } break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_INIT_RT_TABLE_ACK: int tiptr = nsdu_offset + 1; int numPorts = buffer[tiptr++]; routerPortList = new List <RouterPort>(); for (int i = 0; i < numPorts; i++) { RouterPort rp = new RouterPort(); rp.Decode(buffer, ref tiptr); routerPortList.Add(rp); } break; default: // todo break; } } else { // NSDU contains APDU // http://www.bacnetwiki.com/wiki/index.php?title=Network_Layer_Protocol_Control_Information // determine if SNET, SLEN, SADR present apdu_present = true; apdu_offset = npdu_offset + 2; if (sadr != null) { apdu_offset += 2 + 1 + (int)sadr.MACaddress.length; } if (dadr != null) { apdu_offset += 2 + 1 + (int)dadr.MACaddress.length + 1; } apdu_length = length - apdu_offset; if (apdu_length < 0) { _apm.MessagePanic("m0006 Illegal APDU length"); return(false); } // todo - need to extract the apdu for others to refer to. However, APDUs may be customer specific, so extract this as a buffer // only for now, and do some spot checks for relevant functions such as I-Am and Who-Is. apdu_buf = new byte[2000]; Buffer.BlockCopy(buffer, apdu_offset, apdu_buf, 0, apdu_length); // the offset here is the APDU. Start parsing APDU. // todo, decided to leave the enum values unshifted today 11/27/09 pduType = (BACnetEnums.BACNET_PDU_TYPE)(buffer[apdu_offset] & 0xf0); // make sure that we can handle the rest of the packet //if ((buffer[apdu_offset] & 0x0f) != 0 ) //{ // throw new Exception("m0056 - Cannot handle segmented messages yet"); //} int tptr = apdu_offset + 1; if (pduType == BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { // some PDUs have max segs here tptr++; } //_apm.MessageTodo("Remove apdu++"); //apdu_offset++; //apdu_offset++; // now the next byte is the invoke ID invokeID = buffer[tptr++]; switch (pduType) { case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: // todo, offset is not always 1, depends on the flags in the first byte. DecodeUnconfirmedService(apdu_buf, apdu_length); break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_CONFIRMED_SERVICE_REQUEST: DecodeConfirmedService(apdu_buf); break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_SIMPLE_ACK: break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_COMPLEX_ACK: confirmedServiceChoice = (BACnetEnums.BACNET_CONFIRMED_SERVICE)buffer[apdu_offset + 2]; DecodeComplexACK(buffer, apdu_offset); break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_SEGMENT_ACK: _apm.MessageTodo("m0093 - Segment ACK"); errorFlag = true; break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_ERROR: _apm.MessageTodo("m0064 - Error"); errorFlag = true; break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_REJECT: pduRejectReason = (BACnetEnums.BACNET_BACNET_REJECT_REASON)buffer[apdu_offset++]; errorFlag = true; break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_ABORT: _apm.MessageTodo("m0066 - PDU abort"); errorFlag = true; break; default: throw new Exception("m0003 - Illegal PDU type"); } } } catch (Exception ex) { _apm.MessagePanic("m0001 - BACnet Decode Failed " + ex.ToString()); } return(true); }
public void DumpException(AppManager apm) { apm.MessageProtocolError(message); }