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