2 EIBD eib bus access and management daemon
3 Copyright (C) 2005-2008 Martin Koegler <mkoegler@auto.tuwien.ac.at>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <sys/socket.h>
26 #ifdef HAVE_LINUX_NETLINK
27 #include <asm/types.h>
28 #include <linux/netlink.h>
29 #include <linux/rtnetlink.h>
31 #ifdef HAVE_WINDOWS_IPHELPER
37 #if HAVE_BSD_SOURCEINFO
39 #include <net/route.h>
43 GetHostIP (struct sockaddr_in
*sock
, const char *Name
)
48 memset (sock
, 0, sizeof (*sock
));
49 h
= gethostbyname (Name
);
52 #ifdef HAVE_SOCKADDR_IN_LEN
53 sock
->sin_len
= sizeof (*sock
);
55 sock
->sin_family
= h
->h_addrtype
;
56 sock
->sin_addr
.s_addr
= (*((unsigned long *) h
->h_addr_list
[0]));
60 #ifdef HAVE_LINUX_NETLINK
69 GetSourceAddress (const struct sockaddr_in
*dest
, struct sockaddr_in
*src
)
75 memset (&req
, 0, sizeof (req
));
76 memset (src
, 0, sizeof (*src
));
77 s
= socket (PF_NETLINK
, SOCK_DGRAM
, NETLINK_ROUTE
);
81 NLMSG_SPACE (sizeof (req
.r
)) + RTA_LENGTH (sizeof (*dest
));
82 req
.n
.nlmsg_flags
= NLM_F_REQUEST
;
83 req
.n
.nlmsg_type
= RTM_GETROUTE
;
84 req
.r
.rtm_family
= AF_INET
;
85 req
.r
.rtm_dst_len
= 32;
86 a
= (rtattr
*) ((char *) &req
+ NLMSG_SPACE (sizeof (req
.r
)));
87 a
->rta_type
= RTA_DST
;
88 a
->rta_len
= RTA_LENGTH (sizeof (dest
->sin_addr
.s_addr
));
89 memcpy (RTA_DATA (a
), &dest
->sin_addr
.s_addr
,
90 sizeof (dest
->sin_addr
.s_addr
));
91 if (write (s
, &req
, req
.n
.nlmsg_len
) < 0)
93 if (read (s
, &req
, sizeof (req
)) < 0)
96 if (req
.n
.nlmsg_type
== NLMSG_ERROR
)
98 l
= ((struct nlmsghdr
*) &req
)->nlmsg_len
;
101 if (a
->rta_type
== RTA_PREFSRC
102 && RTA_PAYLOAD (a
) == sizeof (src
->sin_addr
.s_addr
))
104 src
->sin_family
= AF_INET
;
105 memcpy (&src
->sin_addr
.s_addr
, RTA_DATA (a
), RTA_PAYLOAD (a
));
114 #ifdef HAVE_WINDOWS_IPHELPER
116 GetSourceAddress (const struct sockaddr_in
*dest
, struct sockaddr_in
*src
)
119 PMIB_IPADDRTABLE tab
;
122 memset (src
, 0, sizeof (*src
));
123 if (GetBestInterface (dest
->sin_addr
.s_addr
, &d
) != NO_ERROR
)
126 tab
= (MIB_IPADDRTABLE
*) malloc (sizeof (MIB_IPADDRTABLE
));
129 if (GetIpAddrTable (tab
, &s
, 0) == ERROR_INSUFFICIENT_BUFFER
)
131 tab
= (MIB_IPADDRTABLE
*) realloc (tab
, s
);
135 if (GetIpAddrTable (tab
, &s
, 0) != NO_ERROR
)
141 for (int i
= 0; i
< tab
->dwNumEntries
; i
++)
142 if (tab
->table
[i
].dwIndex
== d
)
144 src
->sin_family
= AF_INET
;
145 src
->sin_addr
.s_addr
= tab
->table
[i
].dwAddr
;
154 #if HAVE_BSD_SOURCEINFO
157 struct rt_msghdr hdr
;
162 GetSourceAddress (const struct sockaddr_in
*dest
, struct sockaddr_in
*src
)
167 memset (&req
, 0, sizeof (req
));
168 memset (src
, 0, sizeof (*src
));
169 s
= socket (PF_ROUTE
, SOCK_RAW
, 0);
172 req
.hdr
.rtm_msglen
= sizeof (req
) + sizeof (*dest
);
173 req
.hdr
.rtm_version
= RTM_VERSION
;
174 req
.hdr
.rtm_flags
= RTF_UP
;
175 req
.hdr
.rtm_type
= RTM_GET
;
176 req
.hdr
.rtm_addrs
= RTA_DST
| RTA_IFP
;
177 memcpy (cp
, dest
, sizeof (*dest
));
178 if (write (s
, (char *) &req
, req
.hdr
.rtm_msglen
) < 0)
180 if (read (s
, (char *) &req
, sizeof (req
)) < 0)
184 cp
= (char *) (&req
.hdr
+ 1);
185 for (i
= 1; i
; i
<<= 1)
186 if (i
& req
.hdr
.rtm_addrs
)
188 struct sockaddr
*sa
= (struct sockaddr
*) cp
;
191 src
->sin_len
= sizeof (*src
);
192 src
->sin_family
= AF_INET
;
193 src
->sin_addr
.s_addr
=
194 ((struct sockaddr_in
*) sa
)->sin_addr
.s_addr
;
203 EIBNetIPPacket::EIBNetIPPacket ()
209 EIBNetIPPacket::fromPacket (const CArray
& c
)
215 if (c
[0] != 0x6 || c
[1] != 0x10)
217 len
= (c
[4] << 8) | c
[5];
220 p
= new EIBNetIPPacket
;
221 p
->service
= (c
[2] << 8) | c
[3];
222 p
->data
.set (c
.array () + 6, len
- 6);
227 EIBNetIPPacket::ToPacket ()
231 c
.resize (6 + data ());
234 c
[2] = (service
>> 8) & 0xff;
235 c
[3] = (service
) & 0xff;
236 c
[4] = ((data () + 6) >> 8) & 0xff;
237 c
[5] = ((data () + 6)) & 0xff;
243 IPtoEIBNetIP (const struct sockaddr_in
* a
)
249 buf
[2] = (ntohl (a
->sin_addr
.s_addr
) >> 24) & 0xff;
250 buf
[3] = (ntohl (a
->sin_addr
.s_addr
) >> 16) & 0xff;
251 buf
[4] = (ntohl (a
->sin_addr
.s_addr
) >> 8) & 0xff;
252 buf
[5] = (ntohl (a
->sin_addr
.s_addr
) >> 0) & 0xff;
253 buf
[6] = (ntohs (a
->sin_port
) >> 8) & 0xff;
254 buf
[7] = (ntohs (a
->sin_port
) >> 0) & 0xff;
259 EIBnettoIP (const CArray
& buf
, struct sockaddr_in
*a
)
262 memset (a
, 0, sizeof (*a
));
263 if (buf
[0] != 0x8 || buf
[1] != 0x1)
265 ip
= (buf
[2] << 24) | (buf
[3] << 16) | (buf
[4] << 8) | (buf
[5]);
266 port
= (buf
[6] << 8) | (buf
[7]);
267 #ifdef HAVE_SOCKADDR_IN_LEN
268 a
->sin_len
= sizeof (*a
);
270 a
->sin_family
= AF_INET
;
271 a
->sin_port
= htons (port
);
272 a
->sin_addr
.s_addr
= htonl (ip
);
276 EIBNetIPSocket::EIBNetIPSocket (struct sockaddr_in bindaddr
, bool reuseaddr
,
281 TRACEPRINTF (t
, 0, this, "Open");
283 pth_sem_init (&insignal
);
284 pth_sem_init (&outsignal
);
285 getwait
= pth_event (PTH_EVENT_SEM
, &outsignal
);
286 memset (&maddr
, 0, sizeof (maddr
));
287 memset (&sendaddr
, 0, sizeof (sendaddr
));
288 memset (&recvaddr
, 0, sizeof (recvaddr
));
291 fd
= socket (AF_INET
, SOCK_DGRAM
, 0);
298 if (setsockopt (fd
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof (i
)) == -1)
305 if (bind (fd
, (struct sockaddr
*) &bindaddr
, sizeof (bindaddr
)) == -1)
313 TRACEPRINTF (t
, 0, this, "Openend");
316 EIBNetIPSocket::~EIBNetIPSocket ()
318 TRACEPRINTF (t
, 0, this, "Close");
320 pth_event_free (getwait
, PTH_FREE_THIS
);
324 setsockopt (fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &maddr
,
331 EIBNetIPSocket::init ()
337 EIBNetIPSocket::SetMulticast (struct ip_mreq multicastaddr
)
341 maddr
= multicastaddr
;
342 if (setsockopt (fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &maddr
, sizeof (maddr
))
350 EIBNetIPSocket::Send (EIBNetIPPacket p
)
352 struct _EIBNetIP_Send s
;
353 t
->TracePacket (1, this, "Send", p
.data
);
357 pth_sem_inc (&insignal
, 1);
361 EIBNetIPSocket::Get (pth_event_t stop
)
364 pth_event_concat (getwait
, stop
, NULL
);
369 pth_event_isolate (getwait
);
371 if (pth_event_status (getwait
) == PTH_STATUS_OCCURRED
)
373 pth_sem_dec (&outsignal
);
374 t
->TracePacket (1, this, "Recv", outqueue
.top ().data
);
375 return new EIBNetIPPacket (outqueue
.get ());
382 EIBNetIPSocket::Run (pth_sem_t
* stop1
)
388 pth_event_t stop
= pth_event (PTH_EVENT_SEM
, stop1
);
389 pth_event_t input
= pth_event (PTH_EVENT_SEM
, &insignal
);
390 while (pth_event_status (stop
) != PTH_STATUS_OCCURRED
)
392 pth_event_concat (stop
, input
, NULL
);
394 memset (&r
, 0, sizeof (r
));
396 pth_recvfrom_ev (fd
, buf
, sizeof (buf
), 0, (struct sockaddr
*) &r
,
398 if (i
> 0 && rl
== sizeof (r
))
400 if (recvall
== 1 || !memcmp (&r
, &recvaddr
, sizeof (r
)) ||
401 (recvall
== 2 && memcmp (&r
, &localaddr
, sizeof (r
))))
403 t
->TracePacket (0, this, "Recv", i
, buf
);
405 EIBNetIPPacket::fromPacket (CArray (buf
, i
));
410 pth_sem_inc (&outsignal
, 1);
414 pth_event_isolate (stop
);
415 if (!inqueue
.isempty ())
417 const struct _EIBNetIP_Send s
= inqueue
.top ();
418 CArray p
= s
.data
.ToPacket ();
419 t
->TracePacket (0, this, "Send", p
);
421 pth_sendto_ev (fd
, p
.array (), p (), 0,
422 (const struct sockaddr
*) &s
.addr
, sizeof (s
.addr
),
426 pth_sem_dec (&insignal
);
431 pth_event_free (stop
, PTH_FREE_THIS
);
432 pth_event_free (input
, PTH_FREE_THIS
);
435 EIBnet_ConnectRequest::EIBnet_ConnectRequest ()
437 memset (&caddr
, 0, sizeof (caddr
));
438 memset (&daddr
, 0, sizeof (daddr
));
441 EIBNetIPPacket
EIBnet_ConnectRequest::ToPacket ()CONST
448 ca
= IPtoEIBNetIP (&caddr
);
449 da
= IPtoEIBNetIP (&daddr
);
450 p
.service
= CONNECTION_REQUEST
;
451 p
.data
.resize (ca () + da () + 1 + CRI ());
452 p
.data
.setpart (ca
, 0);
453 p
.data
.setpart (da
, ca ());
454 p
.data
[ca () + da ()] = CRI () + 1;
455 p
.data
.setpart (CRI
, ca () + da () + 1);
460 parseEIBnet_ConnectRequest (const EIBNetIPPacket
& p
,
461 EIBnet_ConnectRequest
& r
)
463 if (p
.service
!= CONNECTION_REQUEST
)
467 if (EIBnettoIP (CArray (p
.data
.array (), 8), &r
.caddr
))
469 if (EIBnettoIP (CArray (p
.data
.array () + 8, 8), &r
.daddr
))
471 if (p
.data () - 16 != p
.data
[16])
473 r
.CRI
= CArray (p
.data
.array () + 17, p
.data () - 17);
477 EIBnet_ConnectResponse::EIBnet_ConnectResponse ()
479 memset (&daddr
, 0, sizeof (daddr
));
484 EIBNetIPPacket
EIBnet_ConnectResponse::ToPacket ()CONST
489 da
= IPtoEIBNetIP (&daddr
);
490 p
.service
= CONNECTION_RESPONSE
;
491 p
.data
.resize (da () + CRD () + 3);
494 p
.data
.setpart (da
, 2);
495 p
.data
[da () + 2] = CRD () + 1;
496 p
.data
.setpart (CRD
, da () + 3);
501 parseEIBnet_ConnectResponse (const EIBNetIPPacket
& p
,
502 EIBnet_ConnectResponse
& r
)
504 if (p
.service
!= CONNECTION_RESPONSE
)
508 if (EIBnettoIP (CArray (p
.data
.array () + 2, 8), &r
.daddr
))
510 if (p
.data () - 10 != p
.data
[10])
512 r
.channel
= p
.data
[0];
513 r
.status
= p
.data
[1];
514 r
.CRD
= CArray (p
.data
.array () + 11, p
.data () - 11);
518 EIBnet_ConnectionStateRequest::EIBnet_ConnectionStateRequest ()
520 memset (&caddr
, 0, sizeof (caddr
));
524 EIBNetIPPacket
EIBnet_ConnectionStateRequest::ToPacket ()CONST
529 ca
= IPtoEIBNetIP (&caddr
);
530 p
.service
= CONNECTIONSTATE_REQUEST
;
531 p
.data
.resize (ca () + 2);
534 p
.data
.setpart (ca
, 2);
539 parseEIBnet_ConnectionStateRequest (const EIBNetIPPacket
& p
,
540 EIBnet_ConnectionStateRequest
& r
)
542 if (p
.service
!= CONNECTIONSTATE_REQUEST
)
546 if (EIBnettoIP (CArray (p
.data
.array () + 2, 8), &r
.caddr
))
548 r
.channel
= p
.data
[0];
552 EIBnet_ConnectionStateResponse::EIBnet_ConnectionStateResponse ()
558 EIBNetIPPacket
EIBnet_ConnectionStateResponse::ToPacket ()CONST
562 p
.service
= CONNECTIONSTATE_RESPONSE
;
570 parseEIBnet_ConnectionStateResponse (const EIBNetIPPacket
& p
,
571 EIBnet_ConnectionStateResponse
& r
)
573 if (p
.service
!= CONNECTIONSTATE_RESPONSE
)
577 r
.channel
= p
.data
[0];
578 r
.status
= p
.data
[1];
582 EIBnet_DisconnectRequest::EIBnet_DisconnectRequest ()
584 memset (&caddr
, 0, sizeof (caddr
));
588 EIBNetIPPacket
EIBnet_DisconnectRequest::ToPacket ()CONST
593 ca
= IPtoEIBNetIP (&caddr
);
594 p
.service
= DISCONNECT_REQUEST
;
595 p
.data
.resize (ca () + 2);
598 p
.data
.setpart (ca
, 2);
603 parseEIBnet_DisconnectRequest (const EIBNetIPPacket
& p
,
604 EIBnet_DisconnectRequest
& r
)
606 if (p
.service
!= DISCONNECT_REQUEST
)
610 if (EIBnettoIP (CArray (p
.data
.array () + 2, 8), &r
.caddr
))
612 r
.channel
= p
.data
[0];
616 EIBnet_DisconnectResponse::EIBnet_DisconnectResponse ()
622 EIBNetIPPacket
EIBnet_DisconnectResponse::ToPacket ()CONST
626 p
.service
= DISCONNECT_RESPONSE
;
634 parseEIBnet_DisconnectResponse (const EIBNetIPPacket
& p
,
635 EIBnet_DisconnectResponse
& r
)
637 if (p
.service
!= DISCONNECT_RESPONSE
)
641 r
.channel
= p
.data
[0];
642 r
.status
= p
.data
[1];
646 EIBnet_TunnelRequest::EIBnet_TunnelRequest ()
652 EIBNetIPPacket
EIBnet_TunnelRequest::ToPacket ()CONST
656 p
.service
= TUNNEL_REQUEST
;
657 p
.data
.resize (CEMI () + 4);
662 p
.data
.setpart (CEMI
, 4);
667 parseEIBnet_TunnelRequest (const EIBNetIPPacket
& p
, EIBnet_TunnelRequest
& r
)
669 if (p
.service
!= TUNNEL_REQUEST
)
675 r
.channel
= p
.data
[1];
677 r
.CEMI
.set (p
.data
.array () + 4, p
.data () - 4);
681 EIBnet_TunnelACK::EIBnet_TunnelACK ()
688 EIBNetIPPacket
EIBnet_TunnelACK::ToPacket ()CONST
692 p
.service
= TUNNEL_RESPONSE
;
702 parseEIBnet_TunnelACK (const EIBNetIPPacket
& p
, EIBnet_TunnelACK
& r
)
704 if (p
.service
!= TUNNEL_RESPONSE
)
710 r
.channel
= p
.data
[1];
712 r
.status
= p
.data
[3];
716 EIBnet_DescriptionRequest::EIBnet_DescriptionRequest ()
718 memset (&caddr
, 0, sizeof (caddr
));
721 EIBNetIPPacket
EIBnet_DescriptionRequest::ToPacket ()CONST
726 ca
= IPtoEIBNetIP (&caddr
);
727 p
.service
= DESCRIPTION_REQUEST
;
733 parseEIBnet_DescriptionRequest (const EIBNetIPPacket
& p
,
734 EIBnet_DescriptionRequest
& r
)
736 if (p
.service
!= DESCRIPTION_REQUEST
)
740 if (EIBnettoIP (p
.data
, &r
.caddr
))
746 EIBnet_DescriptionResponse::EIBnet_DescriptionResponse ()
752 memset (&serial
, 0, sizeof (serial
));
753 multicastaddr
.s_addr
= 0;
754 memset (&MAC
, 0, sizeof (MAC
));
755 memset (&name
, 0, sizeof (name
));
758 EIBNetIPPacket
EIBnet_DescriptionResponse::ToPacket ()CONST
762 p
.service
= DESCRIPTION_RESPONSE
;
763 p
.data
.resize (56 + services () * 2);
766 p
.data
[2] = KNXmedium
;
767 p
.data
[3] = devicestatus
;
768 p
.data
[4] = (individual_addr
>> 8) & 0xff;
769 p
.data
[5] = (individual_addr
) & 0xff;
770 p
.data
[6] = (installid
>> 8) & 0xff;
771 p
.data
[7] = (installid
) & 0xff;
772 memcpy (p
.data
.array () + 18, &serial
, 6);
773 memcpy (p
.data
.array () + 14, &multicastaddr
, 4);
774 memcpy (p
.data
.array () + 18, &MAC
, 6);
775 memcpy (p
.data
.array () + 24, &name
, 30);
777 p
.data
[54] = services () * 2 + 2;
779 for (int i
= 0; i
< services (); i
++)
781 p
.data
[56 + i
* 2] = services
[i
].family
;
782 p
.data
[57 + i
* 2] = services
[i
].version
;
784 p
.data
.setpart (optional
, 56 + services () * 2);
789 parseEIBnet_DescriptionResponse (const EIBNetIPPacket
& p
,
790 EIBnet_DescriptionResponse
& r
)
792 if (p
.service
!= DESCRIPTION_RESPONSE
)
800 r
.KNXmedium
= p
.data
[2];
801 r
.devicestatus
= p
.data
[3];
802 r
.individual_addr
= (p
.data
[4] << 8) | p
.data
[5];
803 r
.installid
= (p
.data
[6] << 8) | p
.data
[7];
804 memcpy (&r
.serial
, p
.data
.array () + 8, 6);
805 memcpy (&r
.multicastaddr
, p
.data
.array () + 14, 4);
806 memcpy (&r
.MAC
, p
.data
.array () + 18, 6);
807 memcpy (&r
.name
, p
.data
.array () + 24, 30);
813 if (p
.data
[54] + 54 > p
.data ())
815 r
.services
.resize ((p
.data
[54] / 2) - 1);
816 for (int i
= 0; i
< (p
.data
[54] / 2) - 1; i
++)
818 r
.services
[i
].family
= p
.data
[56 + 2 * i
];
819 r
.services
[i
].version
= p
.data
[57 + 2 * i
];
821 r
.optional
.set (p
.data
.array () + p
.data
[54] + 54,
822 p
.data () - p
.data
[54] - 54);
826 EIBnet_SearchRequest::EIBnet_SearchRequest ()
828 memset (&caddr
, 0, sizeof (caddr
));
831 EIBNetIPPacket
EIBnet_SearchRequest::ToPacket ()CONST
836 ca
= IPtoEIBNetIP (&caddr
);
837 p
.service
= SEARCH_REQUEST
;
843 parseEIBnet_SearchRequest (const EIBNetIPPacket
& p
, EIBnet_SearchRequest
& r
)
845 if (p
.service
!= SEARCH_REQUEST
)
849 if (EIBnettoIP (p
.data
, &r
.caddr
))
855 EIBnet_SearchResponse::EIBnet_SearchResponse ()
861 memset (&serial
, 0, sizeof (serial
));
862 multicastaddr
.s_addr
= 0;
863 memset (&MAC
, 0, sizeof (MAC
));
864 memset (&name
, 0, sizeof (name
));
867 EIBNetIPPacket
EIBnet_SearchResponse::ToPacket ()CONST
872 ca
= IPtoEIBNetIP (&caddr
);
873 p
.service
= SEARCH_RESPONSE
;
874 p
.data
.resize (64 + services () * 2);
875 p
.data
.setpart (ca
, 0);
878 p
.data
[10] = KNXmedium
;
879 p
.data
[11] = devicestatus
;
880 p
.data
[12] = (individual_addr
>> 8) & 0xff;
881 p
.data
[13] = (individual_addr
) & 0xff;
882 p
.data
[14] = (installid
>> 8) & 0xff;
883 p
.data
[15] = (installid
) & 0xff;
884 memcpy (p
.data
.array () + 16, &serial
, 6);
885 memcpy (p
.data
.array () + 22, &multicastaddr
, 4);
886 memcpy (p
.data
.array () + 26, &MAC
, 6);
887 memcpy (p
.data
.array () + 32, &name
, 30);
889 p
.data
[62] = services () * 2 + 2;
891 for (int i
= 0; i
< services (); i
++)
893 p
.data
[64 + i
* 2] = services
[i
].family
;
894 p
.data
[65 + i
* 2] = services
[i
].version
;
900 parseEIBnet_SearchResponse (const EIBNetIPPacket
& p
,
901 EIBnet_SearchResponse
& r
)
903 if (p
.service
!= SEARCH_RESPONSE
)
907 if (EIBnettoIP (CArray (p
.data
.array () + 0, 8), &r
.caddr
))
913 r
.KNXmedium
= p
.data
[10];
914 r
.devicestatus
= p
.data
[11];
915 r
.individual_addr
= (p
.data
[12] << 8) | p
.data
[13];
916 r
.installid
= (p
.data
[14] << 8) | p
.data
[15];
917 memcpy (&r
.serial
, p
.data
.array () + 16, 6);
918 memcpy (&r
.multicastaddr
, p
.data
.array () + 22, 4);
919 memcpy (&r
.MAC
, p
.data
.array () + 26, 6);
920 memcpy (&r
.name
, p
.data
.array () + 32, 30);
926 if (p
.data
[62] + 62 > p
.data ())
928 r
.services
.resize ((p
.data
[62] / 2) - 1);
929 for (int i
= 0; i
< (p
.data
[62] / 2) - 1; i
++)
931 r
.services
[i
].family
= p
.data
[64 + 2 * i
];
932 r
.services
[i
].version
= p
.data
[65 + 2 * i
];