]> git.karo-electronics.de Git - mdnsd.git/blob - netwatch.c
Add futher files to .gitignore
[mdnsd.git] / netwatch.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <poll.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <errno.h>
8
9 #include <arpa/inet.h>
10 #include <linux/if.h>
11 #include <linux/rtnetlink.h>
12
13 #include "netwatch.h"
14 #include "mdnsd.h"
15
16 #define NL_BUFSIZE 8192
17
18
19 /* yeah, these callbacks really shouldn't be global */
20
21 static void *user_data = NULL;
22 static NW_IpChangeCallback   on_ip_change   = NULL;
23 static NW_LinkChangeCallback on_link_change = NULL;
24
25 static void   netwatch_handle_ifaddrmsg (struct nlmsghdr *nl_msg);
26 static void   netwatch_handle_ifinfomsg (struct nlmsghdr *nl_msg);
27
28
29 int
30 netwatch_open (void)
31 {
32     int fd;
33     struct sockaddr_nl  local;
34
35     /* Create Socket */
36     if ((fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
37         return -1;
38
39     memset (&local, 0, sizeof (local));
40     local.nl_family = AF_NETLINK;
41     local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
42
43     if (bind (fd, (struct sockaddr*) &local, sizeof (local)) < 0)
44         return -1;
45
46     if (fcntl (fd, F_SETFL, O_NONBLOCK))
47         return -1;
48
49     return fd;
50 }
51
52 int
53 netwatch_queue_inforequest (int fd)
54 {
55     char buf[NLMSG_LENGTH (sizeof (struct ifaddrmsg))];
56     struct nlmsghdr    *nl_msg;
57
58     bzero (buf, sizeof(buf));
59     nl_msg = (struct nlmsghdr *) buf;
60
61     /* For getting interface addresses */
62     nl_msg->nlmsg_len   = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
63     nl_msg->nlmsg_type  = RTM_GETADDR;
64     nl_msg->nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
65     nl_msg->nlmsg_pid   = getpid ();
66
67     return write (fd, nl_msg, nl_msg->nlmsg_len);
68 }
69
70
71 int
72 netwatch_dispatch (int fd)
73 {
74     struct nlmsghdr    *nl_msg;
75     char buf[NL_BUFSIZE];
76     int len, count;
77
78     count = 0;
79     if ((len = recv (fd, buf, NL_BUFSIZE, 0)) < 0)
80     {
81         return (errno == EAGAIN) ? 0 : -1;
82     }
83
84     for (nl_msg = (struct nlmsghdr *) buf;
85          NLMSG_OK (nl_msg, (unsigned int) len);
86          nl_msg = NLMSG_NEXT (nl_msg, len))
87     {
88         switch (nl_msg->nlmsg_type)
89         {
90             case RTM_NEWADDR:
91             case RTM_DELADDR:
92                 netwatch_handle_ifaddrmsg (nl_msg);
93                 break;
94             case RTM_NEWLINK:
95             case RTM_DELLINK:
96                 netwatch_handle_ifinfomsg (nl_msg);
97                 break;
98             case NLMSG_DONE:
99                 break;
100             default:
101                 fprintf (stderr, "unhandled message (%d)\n",
102                          nl_msg->nlmsg_type);
103                 break;
104         }
105
106         count++;
107     }
108     return count;
109 }
110
111
112 void
113 netwatch_register_callbacks (NW_IpChangeCallback    ip_cb,
114                              NW_LinkChangeCallback  link_cb,
115                              void                  *userdata)
116 {
117     user_data = userdata;
118     on_ip_change = ip_cb;
119     on_link_change = link_cb;
120 }
121
122
123 static void
124 netwatch_handle_ifaddrmsg (struct nlmsghdr *nl_msg)
125 {
126     struct ifaddrmsg *if_msg;
127     struct rtattr    *attrib;
128     int len;
129
130     char  address[100];
131     char *label = NULL;
132
133     if_msg = (struct ifaddrmsg *) NLMSG_DATA (nl_msg);
134
135     if (if_msg->ifa_family != AF_INET)
136         return;
137
138     address[0] = '\0';
139
140     len = IFA_PAYLOAD (nl_msg);
141     for (attrib = IFA_RTA (if_msg);
142          RTA_OK (attrib, len);
143          attrib = RTA_NEXT (attrib, len))
144     {
145         switch (attrib->rta_type)
146         {
147             case IFA_LOCAL:
148                 inet_ntop (AF_INET, RTA_DATA (attrib), address, sizeof (address));
149                 break;
150
151             case IFA_LABEL:
152                 label = (char *) RTA_DATA (attrib);
153                 break;
154
155             default:
156                 /* ignore all other attributes */
157                 break;
158         }
159     }
160
161     /* if we got both a label and an IP address */
162     if (on_ip_change && label && address[0] &&
163         (nl_msg->nlmsg_type == RTM_NEWADDR || nl_msg->nlmsg_type == RTM_DELADDR))
164     {
165         on_ip_change (if_msg->ifa_index, label, address,
166                       nl_msg->nlmsg_type == RTM_NEWADDR, user_data);
167     }
168
169     return;
170 }
171
172
173 static void
174 netwatch_handle_ifinfomsg (struct nlmsghdr *nl_msg)
175 {
176     static unsigned long have_state = 0;
177     static unsigned long linkstate = 0;
178     struct ifinfomsg *ifi;
179
180     ifi = (struct ifinfomsg *) NLMSG_DATA (nl_msg);
181
182     if (on_link_change && ((unsigned int)ifi->ifi_index < sizeof (unsigned long) * 8))
183     {
184         if (!((1 << ifi->ifi_index) & have_state) ||
185             ((ifi->ifi_flags & IFF_RUNNING) && !((1 << ifi->ifi_index) & linkstate)) ||
186             (!(ifi->ifi_flags & IFF_RUNNING) && ((1 << ifi->ifi_index) & linkstate)))
187         {
188             have_state |= (1 << ifi->ifi_index);
189             if (ifi->ifi_flags & IFF_RUNNING)
190                 linkstate |= (1 << ifi->ifi_index);
191             else
192                 linkstate &= ~(1 << ifi->ifi_index);
193
194             on_link_change (ifi->ifi_index,
195                             ifi->ifi_flags & IFF_RUNNING ? 1 : 0,
196                             user_data);
197         }
198     }
199 }
200
201
202
203 #ifdef NETWATCH_MAIN
204 void
205 print_ip_change (int    link_index,
206                  char  *label,
207                  char  *ipaddr,
208                  int    add,
209                  void  *UNUSED(user_data))
210 {
211     fprintf (stdout, "Link No. %d %s address %s (label %s)\n",
212              link_index, add ? "got" : "lost", ipaddr, label);
213 }
214
215
216 void print_link_change (int   link_index,
217                         int   running,
218                         void *UNUSED(user_data))
219 {
220     fprintf (stdout, "Link No. %d is %sconnected\n",
221              link_index, running ? "" : "no longer ");
222 }
223
224
225 int
226 main (int UNUSED(argc), char **UNUSED(argv))
227 {
228     struct pollfd       pollfd;
229     int fd;
230
231     netwatch_register_callbacks (print_ip_change, print_link_change, NULL);
232
233     fd = netwatch_open ();
234     if (fd < 0)
235     {
236         perror ("Error opening netlink socket");
237         exit (1);
238     }
239
240     netwatch_queue_inforequest (fd);
241
242     while (1)
243     {
244         pollfd.fd = fd;
245         pollfd.events = POLLIN;
246         pollfd.revents = 0;
247
248         if (poll (&pollfd, 1, 20000))
249             netwatch_dispatch (fd);
250     }
251
252     close (fd);
253     return 0;
254 }
255 #endif