Use RTNL newlink callback for link changes
[connman] / plugins / ethernet.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <linux/if.h>
31 #include <linux/netlink.h>
32 #include <linux/rtnetlink.h>
33
34 #include <connman/plugin.h>
35 #include <connman/driver.h>
36 #include <connman/rtnl.h>
37 #include <connman/log.h>
38
39 struct ethernet_data {
40         int index;
41         unsigned flags;
42 };
43
44 static GStaticMutex ethernet_mutex = G_STATIC_MUTEX_INIT;
45 static GSList *ethernet_list = NULL;
46
47 static void ethernet_newlink(unsigned short type, int index,
48                                         unsigned flags, unsigned change)
49 {
50         GSList *list;
51
52         g_static_mutex_lock(&ethernet_mutex);
53
54         for (list = ethernet_list; list; list = list->next) {
55                 struct connman_element *element = list->data;
56                 struct connman_element *netdev;
57                 struct ethernet_data *ethernet;
58
59                 ethernet = connman_element_get_data(element);
60                 if (ethernet == NULL)
61                         continue;
62
63                 if (ethernet->index != index)
64                         continue;
65
66                 if ((ethernet->flags & IFF_RUNNING) == (flags & IFF_RUNNING))
67                         continue;
68
69                 ethernet->flags = flags;
70
71                 if (ethernet->flags & IFF_RUNNING) {
72                         DBG("carrier on");
73
74                         netdev = connman_element_create(NULL);
75                         if (netdev != NULL) {
76                                 netdev->type    = CONNMAN_ELEMENT_TYPE_DEVICE;
77                                 netdev->subtype = CONNMAN_ELEMENT_SUBTYPE_NETWORK;
78                                 netdev->index   = element->index;
79
80                                 connman_element_register(netdev, element);
81                         }
82                 } else {
83                         DBG("carrier off");
84
85                         connman_element_unregister_children(element);
86                 }
87         }
88
89         g_static_mutex_unlock(&ethernet_mutex);
90 }
91
92 static struct connman_rtnl ethernet_rtnl = {
93         .name           = "ethernet",
94         .newlink        = ethernet_newlink,
95 };
96
97 static int iface_up(struct ethernet_data *ethernet)
98 {
99         struct ifreq ifr;
100         int sk, err;
101
102         DBG("index %d flags %d", ethernet->index, ethernet->flags);
103
104         sk = socket(PF_INET, SOCK_DGRAM, 0);
105         if (sk < 0)
106                 return -errno;
107
108         memset(&ifr, 0, sizeof(ifr));
109         ifr.ifr_ifindex = ethernet->index;
110
111         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
112                 err = -errno;
113                 goto done;
114         }
115
116         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
117                 err = -errno;
118                 goto done;
119         }
120
121         if (ifr.ifr_flags & IFF_UP) {
122                 err = -EALREADY;
123                 goto done;
124         }
125
126         ifr.ifr_flags |= IFF_UP;
127
128         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
129                 err = -errno;
130                 goto done;
131         }
132
133         err = 0;
134
135 done:
136         close(sk);
137
138         return err;
139 }
140
141 static int iface_down(struct ethernet_data *ethernet)
142 {
143         struct ifreq ifr;
144         int sk, err;
145
146         DBG("index %d flags %d", ethernet->index, ethernet->flags);
147
148         sk = socket(PF_INET, SOCK_DGRAM, 0);
149         if (sk < 0)
150                 return -errno;
151
152         memset(&ifr, 0, sizeof(ifr));
153         ifr.ifr_ifindex = ethernet->index;
154
155         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
156                 err = -errno;
157                 goto done;
158         }
159
160         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
161                 err = -errno;
162                 goto done;
163         }
164
165         if (!(ifr.ifr_flags & IFF_UP)) {
166                 err = -EALREADY;
167                 goto done;
168         }
169
170         ifr.ifr_flags &= ~IFF_UP;
171
172         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
173                 err = -errno;
174         else
175                 err = 0;
176
177 done:
178         close(sk);
179
180         return err;
181 }
182
183 static int ethernet_probe(struct connman_element *element)
184 {
185         struct ethernet_data *ethernet;
186
187         DBG("element %p name %s", element, element->name);
188
189         ethernet = g_try_new0(struct ethernet_data, 1);
190         if (ethernet == NULL)
191                 return -ENOMEM;
192
193         g_static_mutex_lock(&ethernet_mutex);
194         ethernet_list = g_slist_append(ethernet_list, element);
195         g_static_mutex_unlock(&ethernet_mutex);
196
197         connman_element_set_data(element, ethernet);
198
199         ethernet->index = element->index;
200
201         iface_up(ethernet);
202
203         connman_rtnl_send_getlink();
204
205         return 0;
206 }
207
208 static void ethernet_remove(struct connman_element *element)
209 {
210         struct ethernet_data *ethernet = connman_element_get_data(element);
211
212         DBG("element %p name %s", element, element->name);
213
214         connman_element_set_data(element, NULL);
215
216         iface_down(ethernet);
217
218         g_static_mutex_lock(&ethernet_mutex);
219         ethernet_list = g_slist_remove(ethernet_list, element);
220         g_static_mutex_unlock(&ethernet_mutex);
221
222         g_free(ethernet);
223 }
224
225 static struct connman_driver ethernet_driver = {
226         .name           = "ethernet",
227         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
228         .subtype        = CONNMAN_ELEMENT_SUBTYPE_ETHERNET,
229         .probe          = ethernet_probe,
230         .remove         = ethernet_remove,
231 };
232
233 static int ethernet_init(void)
234 {
235         int err;
236
237         err = connman_rtnl_register(&ethernet_rtnl);
238         if (err < 0)
239                 return err;
240
241         err = connman_driver_register(&ethernet_driver);
242         if (err < 0) {
243                 connman_rtnl_unregister(&ethernet_rtnl);
244                 return err;
245         }
246
247         return 0;
248 }
249
250 static void ethernet_exit(void)
251 {
252         connman_driver_unregister(&ethernet_driver);
253
254         connman_rtnl_unregister(&ethernet_rtnl);
255 }
256
257 CONNMAN_PLUGIN_DEFINE("ethernet", "Ethernet interface plugin", VERSION,
258                                                 ethernet_init, ethernet_exit)