// Encode is called by internal services if the BBMD is also an active device public int Encode(byte[] buffer, int offset, BacnetBvlcV6Functions function, int msg_length, BacnetAddress address) { // offset always 0, we are the first after udp First7BytesHeaderEncode(buffer, function, msg_length); // BBMD service if ((function == BacnetBvlcV6Functions.BVLC_ORIGINAL_BROADCAST_NPDU) && (BBMD_FD_ServiceActivated == true)) { Net.IPEndPoint me = MyTransport.LocalEndPoint; BacnetAddress Bacme; BacnetIpV6UdpProtocolTransport.Convert(me, out Bacme); Array.Copy(VMAC, Bacme.VMac, 3); Forward_NPDU(buffer, msg_length, false, me, Bacme); // send to all BBMDs and FDs return(7); // ready to send } if (function == BacnetBvlcV6Functions.BVLC_ORIGINAL_UNICAST_NPDU) { buffer[7] = address.VMac[0]; buffer[8] = address.VMac[1]; buffer[9] = address.VMac[2]; return(10); // ready to send } return(0); // ? }
// Never tested private void Forward_NPDU(byte[] buffer, int msg_length, bool ToGlobalBroadcast, Net.IPEndPoint EPsender, BacnetAddress BacSender) { // Forms the forwarded NPDU from the original (broadcast npdu), and send it to all // copy, 18 bytes shifted (orignal bvlc header : 7 bytes, new one : 25 bytes) byte[] b = new byte[msg_length + 18]; // normaly only 'small' frames are present here, so no need to check if it's to big for Udp Array.Copy(buffer, 0, b, 18, msg_length); // 7 bytes for the BVLC Header, with the embedded 6 bytes IP:Port of the original sender First7BytesHeaderEncode(b, BacnetBvlcV6Functions.BVLC_FORWARDED_NPDU, msg_length + 18); // replace my Vmac by the orignal source vmac Array.Copy(BacSender.VMac, 0, b, 4, 3); // Add IpV6 endpoint Array.Copy(BacSender.adr, 0, b, 7, 18); // Send To BBMD SendToBBMDs(b, msg_length + 18); // Send To FD, except the sender SendToFDs(b, msg_length + 18, EPsender); // Broadcast if required if (ToGlobalBroadcast == true) { IPEndPoint ep; BacnetIpV6UdpProtocolTransport.Convert(BroadcastAdd, out ep); MyTransport.Send(b, msg_length + 18, ep); } }
public BVLCV6(BacnetIpV6UdpProtocolTransport Transport, int VMAC) { MyTransport = Transport; BroadcastAdd = MyTransport.GetBroadcastAddress(); if (VMAC == -1) { RandomVmac = true; new Random().NextBytes(this.VMAC); this.VMAC[0] = (byte)((this.VMAC[0] & 0x7F) | 0x40); // ensure 01xxxxxx on the High byte // Open with default interface specified, cannot send it or // it will generate an uncheckable continuous local loopback if (!MyTransport.LocalEndPoint.ToString().Contains("[::]")) { SendAddressResolutionRequest(this.VMAC); } else { RandomVmac = false; // back to false avoiding loop back } } else // Device Id is the Vmac Id { this.VMAC[0] = (byte)((VMAC >> 16) & 0x3F); // ensure the 2 high bits are 0 on the High byte this.VMAC[1] = (byte)((VMAC >> 8) & 0xFF); this.VMAC[2] = (byte)(VMAC & 0xFF); // unicity is guaranteed by the end user ! } }
// Encode is called by internal services if the BBMD is also an active device public int Encode(byte[] buffer, BacnetBvlcV6Functions function, int msgLength, BacnetAddress address) { // offset always 0, we are the first after udp First7BytesHeaderEncode(buffer, function, msgLength); // BBMD service if (function == BacnetBvlcV6Functions.BVLC_ORIGINAL_BROADCAST_NPDU && _bbmdFdServiceActivated) { var me = _myTransport.LocalEndPoint; BacnetIpV6UdpProtocolTransport.Convert(me, out var bacme); Array.Copy(VMAC, bacme.VMac, 3); Forward_NPDU(buffer, msgLength, false, me, bacme); // send to all BBMDs and FDs return(7); // ready to send } if (function != BacnetBvlcV6Functions.BVLC_ORIGINAL_UNICAST_NPDU) { return(0); // ? } buffer[7] = address.VMac[0]; buffer[8] = address.VMac[1]; buffer[9] = address.VMac[2]; return(10); // ready to send }
public override bool Equals(object obj) { if (!(obj is BacnetIpV6UdpProtocolTransport)) { return(false); } BacnetIpV6UdpProtocolTransport a = (BacnetIpV6UdpProtocolTransport)obj; return(a.m_port == m_port); }
// quite the same frame as the previous one private void SendAddressResolutionRequest(byte[] vMacDest) { BacnetIpV6UdpProtocolTransport.Convert(_broadcastAdd, out IPEndPoint ep); var b = new byte[10]; First7BytesHeaderEncode(b, BacnetBvlcV6Functions.BVLC_ADDRESS_RESOLUTION, 10); Array.Copy(vMacDest, 0, b, 7, 3); _myTransport.Send(b, 10, ep); }
// Decode is called each time an Udp Frame is received public int Decode(byte[] buffer, int offset, out BacnetBvlcV6Functions function, out int msg_length, System.Net.IPEndPoint sender, BacnetAddress remote_address) { // offset always 0, we are the first after udp // and a previous test by the caller guaranteed at least 4 bytes into the buffer function = (BacnetBvlcV6Functions)buffer[1]; msg_length = (buffer[2] << 8) | (buffer[3] << 0); if ((buffer[0] != BVLL_TYPE_BACNET_IPV6) || (buffer.Length != msg_length)) { return(-1); } Array.Copy(buffer, 4, remote_address.VMac, 0, 3); switch (function) { case BacnetBvlcV6Functions.BVLC_RESULT: return(9); // only for the upper layers case BacnetBvlcV6Functions.BVLC_ORIGINAL_UNICAST_NPDU: return(10); // only for the upper layers case BacnetBvlcV6Functions.BVLC_ORIGINAL_BROADCAST_NPDU: // Send to FDs & BBMDs, not broadcast or it will be made twice ! if (BBMD_FD_ServiceActivated == true) { Forward_NPDU(buffer, msg_length, false, sender, remote_address); } return(7); // also for the upper layers case BacnetBvlcV6Functions.BVLC_ADDRESS_RESOLUTION: // need to verify that the VMAC is mine if ((VMAC[0] == buffer[7]) && (VMAC[1] == buffer[8]) && (VMAC[2] == buffer[9])) { // coming from myself ? avoid loopback if (!MyTransport.LocalEndPoint.Equals(sender)) { SendAddressResolutionAck(sender, remote_address.VMac, BacnetBvlcV6Functions.BVLC_ADDRESS_RESOLUTION_ACK); } } return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_FORWARDED_ADDRESS_RESOLUTION: // no need to verify the target VMAC, should be OK SendAddressResolutionAck(sender, remote_address.VMac, BacnetBvlcV6Functions.BVLC_ADDRESS_RESOLUTION_ACK); return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_ADDRESS_RESOLUTION_ACK: // adresse conflict if ((VMAC[0] == buffer[4]) && (VMAC[1] == buffer[5]) && (VMAC[2] == buffer[6]) && RandomVmac) { new Random().NextBytes(this.VMAC); this.VMAC[0] = (byte)((this.VMAC[0] & 0x7F) | 0x40); SendAddressResolutionRequest(VMAC); } return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_VIRTUAL_ADDRESS_RESOLUTION: SendAddressResolutionAck(sender, remote_address.VMac, BacnetBvlcV6Functions.BVLC_VIRTUAL_ADDRESS_RESOLUTION_ACK); return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_VIRTUAL_ADDRESS_RESOLUTION_ACK: return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_FORWARDED_NPDU: if (MyTransport.LocalEndPoint.Equals(sender)) { return(0); } // certainly TODO the same code I've put in the IPV4 implementation if ((BBMD_FD_ServiceActivated == true) && (msg_length >= 25)) { bool ret; lock (BBMDs) ret = BBMDs.Exists(items => items.Equals(sender)); // verify sender presence in the table // avoid also loopback if (ret) // message from a know BBMD address, sent to all FDs and broadcast { SendToFDs(buffer, msg_length); // send without modification // Assume all BVLC_FORWARDED_NPDU are directly sent to me in the // unicast mode and not by the way of the multicast address // If not, it's not really a big problem, devices on the local net will // receive two times the message (after all it's just WhoIs, Iam, ...) IPEndPoint ep; BacnetIpV6UdpProtocolTransport.Convert(BroadcastAdd, out ep); MyTransport.Send(buffer, msg_length, ep); } } return(25); // for the upper layers case BacnetBvlcV6Functions.BVLC_REGISTER_FOREIGN_DEVICE: if ((BBMD_FD_ServiceActivated == true) && (msg_length == 9)) { int TTL = (buffer[7] << 8) + buffer[8]; // unit is second RegisterForeignDevice(sender, TTL); SendResult(sender, BacnetBvlcV6Results.SUCCESSFUL_COMPLETION); // ack } return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_SECURE_BVLC: return(0); // not for the upper layers case BacnetBvlcV6Functions.BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: // Sent by a Foreign Device, not a BBMD if (BBMD_FD_ServiceActivated == true) { // Send to FDs except the sender, BBMDs and broadcast lock (ForeignDevices) { if (ForeignDevices.Exists(item => item.Key.Equals(sender))) // verify previous registration { Forward_NPDU(buffer, msg_length, true, sender, remote_address); } else { SendResult(sender, BacnetBvlcV6Results.DISTRIBUTE_BROADCAST_TO_NETWORK_NAK); } } } return(0); // not for the upper layers // error encoding function or experimental one default: return(-1); } }