// 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); // ? }
private System.Net.IPEndPoint BBMDSentAdd(Net.IPEndPoint BBMD, Net.IPAddress Mask) { byte[] bm = Mask.GetAddressBytes(); byte[] bip = BBMD.Address.GetAddressBytes(); /* annotation in Steve Karg bacnet stack : * * The B/IP address to which the Forwarded-NPDU message is * sent is formed by inverting the broadcast distribution * mask in the BDT entry and logically ORing it with the * BBMD address of the same entry. This process * produces either the directed broadcast address of the remote * subnet or the unicast address of the BBMD on that subnet * depending on the contents of the broadcast distribution * mask. * * remark from me : * for instance remote BBMD 192.168.0.1 - mask 255.255.255.255 * messages are forward directly to 192.168.0.1 * remote BBMD 192.168.0.1 - mask 255.255.255.0 * messages are forward to 192.168.0.255, ie certainly the local broadcast * address, but these datagrams are generaly destroy by the final IP router */ for (int i = 0; i < bm.Length; i++) { bip[i] = (byte)(bip[i] | (~bm[i])); } return(new System.Net.IPEndPoint(new System.Net.IPAddress(bip), BBMD.Port)); }
private void Open() { System.Net.Sockets.UdpClient multicastListener = null; if (!m_exclusive_port) { /* We need a shared multicast "listen" port. This is the 0xBAC0 port */ /* This will enable us to have more than 1 client, on the same machine. Perhaps it's not that important though. */ /* We (might) only receive the multicast on this. Any unicasts to this might be eaten by another local client */ if (m_shared_conn == null) { m_shared_conn = new Net.Sockets.UdpClient(System.Net.Sockets.AddressFamily.InterNetworkV6); m_shared_conn.ExclusiveAddressUse = false; m_shared_conn.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, true); System.Net.EndPoint ep = new System.Net.IPEndPoint(System.Net.IPAddress.IPv6Any, m_port); if (!string.IsNullOrEmpty(m_local_endpoint)) { ep = new System.Net.IPEndPoint(Net.IPAddress.Parse(m_local_endpoint), m_port); } m_shared_conn.Client.Bind(ep); multicastListener = m_shared_conn; } /* This is our own exclusive port. We'll recieve everything sent to this. */ /* So this is how we'll present our selves to the world */ if (m_exclusive_conn == null) { System.Net.EndPoint ep = new Net.IPEndPoint(System.Net.IPAddress.IPv6Any, 0); if (!string.IsNullOrEmpty(m_local_endpoint)) { ep = new Net.IPEndPoint(Net.IPAddress.Parse(m_local_endpoint), 0); } m_exclusive_conn = new Net.Sockets.UdpClient((Net.IPEndPoint)ep); } } else { System.Net.EndPoint ep = new Net.IPEndPoint(System.Net.IPAddress.IPv6Any, m_port); if (!string.IsNullOrEmpty(m_local_endpoint)) { ep = new Net.IPEndPoint(Net.IPAddress.Parse(m_local_endpoint), m_port); } m_exclusive_conn = new Net.Sockets.UdpClient(System.Net.Sockets.AddressFamily.InterNetworkV6); m_exclusive_conn.ExclusiveAddressUse = true; m_exclusive_conn.Client.Bind((Net.IPEndPoint)ep); multicastListener = m_exclusive_conn; } multicastListener.JoinMulticastGroup(IPAddress.Parse("[FF02::BAC0]")); multicastListener.JoinMulticastGroup(IPAddress.Parse("[FF04::BAC0]")); multicastListener.JoinMulticastGroup(IPAddress.Parse("[FF05::BAC0]")); multicastListener.JoinMulticastGroup(IPAddress.Parse("[FF08::BAC0]")); multicastListener.JoinMulticastGroup(IPAddress.Parse("[FF0E::BAC0]")); // If this option is enabled Yabe cannot see itself ! // multicastListener.MulticastLoopback = false; bvlc = new BVLCV6(this, m_VMac); }
// Used to initiate the BBMD & FD behaviour, if BBMD is null it start the FD activity only public void AddBBMDPeer(Net.IPEndPoint BBMD) { BBMD_FD_ServiceActivated = true; if (BBMD != null) { lock (BBMDs) BBMDs.Add(BBMD); } }
// Used to initiate the BBMD & FD behaviour, if BBMD is null it start the FD activity only public void AddBBMDPeer(Net.IPEndPoint BBMD, Net.IPAddress Mask) { BBMD_FD_ServiceActivated = true; if (BBMD != null) { lock (BBMDs) BBMDs.Add(new KeyValuePair <System.Net.IPEndPoint, System.Net.IPAddress>(BBMD, Mask)); } }
public BacnetAddress GetBroadcastAddress() { BacnetAddress ret; // could be FF08, FF05, FF04, FF02 System.Net.IPEndPoint ep = new Net.IPEndPoint(IPAddress.Parse("[FF0E::BAC0]"), m_port); Convert(ep, out ret); ret.net = 0xFFFF; return(ret); }
// A lot of problems on Mono (Raspberry) to get the correct broadcast @ // so this method is overridable (this allows the implementation of operating system specific code) // Marc solution http://stackoverflow.com/questions/8119414/how-to-query-the-subnet-masks-using-mono-on-linux for instance // protected virtual BacnetAddress _GetBroadcastAddress() { // general broadcast by default if nothing better is found System.Net.IPEndPoint ep = new Net.IPEndPoint(System.Net.IPAddress.Parse("255.255.255.255"), m_port); Net.NetworkInformation.UnicastIPAddressInformation ipAddr = null; if (LocalEndPoint.Address.ToString() == "0.0.0.0") { ipAddr = GettAddress_DefaultInterface(); } else { // restricted local broadcast (directed ... routable) foreach (System.Net.NetworkInformation.NetworkInterface adapter in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()) { foreach (System.Net.NetworkInformation.UnicastIPAddressInformation ip in adapter.GetIPProperties().UnicastAddresses) { if (LocalEndPoint.Address.Equals(ip.Address)) { ipAddr = ip; break; } } } } if (ipAddr != null) { try { string[] strCurrentIP = ipAddr.Address.ToString().Split('.'); string[] strIPNetMask = ipAddr.IPv4Mask.ToString().Split('.'); StringBuilder BroadcastStr = new StringBuilder(); for (int i = 0; i < 4; i++) { BroadcastStr.Append(((byte)(int.Parse(strCurrentIP[i]) | ~int.Parse(strIPNetMask[i]))).ToString()); if (i != 3) { BroadcastStr.Append('.'); } } ep = new Net.IPEndPoint(System.Net.IPAddress.Parse(BroadcastStr.ToString()), m_port); } catch { } //On mono IPv4Mask feature not implemented } BacnetAddress broadcast; Convert(ep, out broadcast); broadcast.net = 0xFFFF; return(broadcast); }
// Send a Frame to each registered foreign devices, except the original sender private void SendToFDs(byte[] buffer, int msg_length, Net.IPEndPoint EPsender = null) { lock (ForeignDevices) { // remove oldest Device entries (Time expiration > TTL + 30s delay) ForeignDevices.Remove(ForeignDevices.Find(item => DateTime.Now > item.Value)); // Send to all others, except the original sender foreach (KeyValuePair <System.Net.IPEndPoint, DateTime> client in ForeignDevices) { if (!(client.Key.Equals(EPsender))) { MyTransport.Send(buffer, msg_length, client.Key); } } } }
// Send a Frame to each registered foreign devices, except the original sender private void SendToFDs(byte[] buffer, int msgLength, Net.IPEndPoint ePsender = null) { lock (_foreignDevices) { // remove oldest Device entries (Time expiration > TTL + 30s delay) _foreignDevices.Remove(_foreignDevices.Find(item => DateTime.Now > item.Value)); // Send to all others, except the original sender foreach (var client in _foreignDevices) { if (!client.Key.Equals(ePsender)) { _myBbmdTransport.Send(buffer, msgLength, client.Key); } } } }
static string GetRemoteEndpointAddressPort(Net.IPEndPoint iPEndPoint) { //We really don't want any exceptions out of TraceUtility. if (iPEndPoint != null) { try { return(iPEndPoint.Address.ToString() + ":" + iPEndPoint.Port); } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } //ignore and continue with all non-fatal exceptions. } } return(string.Empty); }
// Encode is called by internal services if the BBMD is also an active device public int Encode(byte[] buffer, int offset, BacnetBvlcFunctions function, int msg_length) { // offset always 0, we are the first after udp // do the job First4BytesHeaderEncode(buffer, function, msg_length); // optional BBMD service if ((BBMD_FD_ServiceActivated == true) && (function == BacnetBvlcFunctions.BVLC_ORIGINAL_BROADCAST_NPDU)) { Net.IPEndPoint me = MyBBMDTransport.LocalEndPoint; // just sometime working, enable to get the local ep, always 0.0.0.0 if the socket is open with // System.Net.IPAddress.Any // So in this case don't send a bad message if ((me.Address.ToString() != "0.0.0.0")) { Forward_NPDU(buffer, msg_length, false, me); // send to all BBMDs and FDs } } return(4); // ready to send }
// 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); } }
private void Forward_NPDU(byte[] buffer, int msg_length, bool ToGlobalBroadcast, Net.IPEndPoint EPsender) { // Forms the forwarded NPDU from the original one, and send it to all // orignal - 4 bytes BVLC - NPDU - APDU // change to - 10 bytes BVLC - NPDU - APDU // copy, 6 bytes shifted byte[] b = new byte[msg_length + 6]; // normaly only 'small' frames are present here, so no need to check if it's to big for Udp Array.Copy(buffer, 0, b, 6, msg_length); // 10 bytes for the BVLC Header, with the embedded 6 bytes IP:Port of the original sender First4BytesHeaderEncode(b, BacnetBvlcFunctions.BVLC_FORWARDED_NPDU, msg_length + 6); BacnetAddress BacSender; BacnetIpUdpProtocolTransport.Convert(EPsender, out BacSender); // to embbed in the forward BVLC header for (int i = 0; i < BacSender.adr.Length; i++) { b[4 + i] = BacSender.adr[i]; } // To BBMD SendToBBMDs(b, msg_length + 6); // To FD, except the sender SendToFDs(b, msg_length + 6, EPsender); // Broadcast if required if (ToGlobalBroadcast == true) { MyBBMDTransport.Send(b, msg_length + 6, new Net.IPEndPoint(Net.IPAddress.Parse(BroadcastAdd), MyBBMDTransport.SharedPort)); } }
private void OnReceiveData(IAsyncResult asyncResult) { System.Net.Sockets.UdpClient conn = (System.Net.Sockets.UdpClient)asyncResult.AsyncState; try { System.Net.IPEndPoint ep = new Net.IPEndPoint(System.Net.IPAddress.Any, 0); byte[] local_buffer; int rx = 0; try { local_buffer = conn.EndReceive(asyncResult, ref ep); rx = local_buffer.Length; } catch (Exception) // ICMP port unreachable { //restart data receive conn.BeginReceive(OnReceiveData, conn); return; } if (rx == 0) // Empty frame : port scanner maybe { //restart data receive conn.BeginReceive(OnReceiveData, conn); return; } try { // restart data reception before the job is finish ... could be dangerous conn.BeginReceive(OnReceiveData, conn); //verify message BacnetAddress remote_address; Convert((System.Net.IPEndPoint)ep, out remote_address); BacnetBvlcFunctions function; int msg_length; if (rx < BVLC.BVLC_HEADER_LENGTH) { Trace.TraceWarning("Some garbage data got in"); } else { // Basic Header lenght int HEADER_LENGTH = bvlc.Decode(local_buffer, 0, out function, out msg_length, ep); if (HEADER_LENGTH == -1) { Trace.WriteLine("Unknow BVLC Header"); return; } // response to BVLC_REGISTER_FOREIGN_DEVICE (could be BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK ... but we are not a BBMD, don't care) if (function == BacnetBvlcFunctions.BVLC_RESULT) { Trace.WriteLine("Receive Register as Foreign Device Response"); } // a BVLC_FORWARDED_NPDU frame by a BBMD, change the remote_address to the original one (stored in the BVLC header) // we don't care about the BBMD address if (function == BacnetBvlcFunctions.BVLC_FORWARDED_NPDU) { long ip = ((long)local_buffer[7] << 24) + ((long)local_buffer[6] << 16) + ((long)local_buffer[5] << 8) + (long)local_buffer[4]; int port = (local_buffer[8] << 8) + local_buffer[9]; // 0xbac0 maybe ep = new Net.IPEndPoint(ip, port); Convert((System.Net.IPEndPoint)ep, out remote_address); } if ((function == BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU) || (function == BacnetBvlcFunctions.BVLC_ORIGINAL_BROADCAST_NPDU) || (function == BacnetBvlcFunctions.BVLC_FORWARDED_NPDU)) { //send to upper layers if ((MessageRecieved != null) && (rx > HEADER_LENGTH)) { MessageRecieved(this, local_buffer, HEADER_LENGTH, rx - HEADER_LENGTH, remote_address); } } } } catch (Exception ex) { Trace.TraceError("Exception in udp recieve: " + ex.Message); } finally { //restart data receive //conn.BeginReceive(OnReceiveData, conn); } } catch (Exception ex) { //restart data receive if (conn.Client != null) { Trace.TraceError("Exception in Ip OnRecieveData: " + ex.Message); conn.BeginReceive(OnReceiveData, conn); } } }
private void Open() { if (!m_exclusive_port) { /* We need a shared broadcast "listen" port. This is the 0xBAC0 port */ /* This will enable us to have more than 1 client, on the same machine. Perhaps it's not that important though. */ /* We (might) only recieve the broadcasts on this. Any unicasts to this might be eaten by another local client */ if (m_shared_conn == null) { m_shared_conn = new Net.Sockets.UdpClient(); m_shared_conn.ExclusiveAddressUse = false; m_shared_conn.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, true); System.Net.EndPoint ep = new System.Net.IPEndPoint(System.Net.IPAddress.Any, m_port); if (!string.IsNullOrEmpty(m_local_endpoint)) { ep = new System.Net.IPEndPoint(Net.IPAddress.Parse(m_local_endpoint), m_port); } m_shared_conn.Client.Bind(ep); m_shared_conn.DontFragment = m_dont_fragment; } /* This is our own exclusive port. We'll receive everything sent to this. */ /* So this is how we'll present our selves to the world */ if (m_exclusive_conn == null) { System.Net.EndPoint ep = new Net.IPEndPoint(System.Net.IPAddress.Any, 0); if (!string.IsNullOrEmpty(m_local_endpoint)) { ep = new Net.IPEndPoint(Net.IPAddress.Parse(m_local_endpoint), 0); } // Opens the socket, udp will choose the Port number m_exclusive_conn = new Net.Sockets.UdpClient((Net.IPEndPoint)ep); // Gets the Endpoint : the assigned Udp port number in fact ep = m_exclusive_conn.Client.LocalEndPoint; // closes the socket m_exclusive_conn.Close(); // Re-opens it with the freeed port number, to be sure it's a real active/server socket // which cannot be disarmed for listen by .NET for incoming call after a few inactivity // minutes ... yes it's like this at least on several systems m_exclusive_conn = new Net.Sockets.UdpClient((Net.IPEndPoint)ep); m_exclusive_conn.DontFragment = m_dont_fragment; m_exclusive_conn.EnableBroadcast = true; } } else { System.Net.EndPoint ep = new Net.IPEndPoint(System.Net.IPAddress.Any, m_port); if (!string.IsNullOrEmpty(m_local_endpoint)) { ep = new Net.IPEndPoint(Net.IPAddress.Parse(m_local_endpoint), m_port); } m_exclusive_conn = new Net.Sockets.UdpClient(); m_exclusive_conn.ExclusiveAddressUse = true; m_exclusive_conn.Client.Bind((Net.IPEndPoint)ep); m_exclusive_conn.DontFragment = m_dont_fragment; m_exclusive_conn.EnableBroadcast = true; } bvlc = new BVLC(this); }