Convert Ethernet plugin into a device driver
[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/device.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 GSList *ethernet_list = NULL;
45
46 static void update_power(struct connman_device *device, unsigned flags)
47 {
48         if (flags & IFF_UP) {
49                 DBG("power on");
50
51                 connman_device_set_powered(device, TRUE);
52         } else {
53                 DBG("power off");
54
55                 connman_device_set_powered(device, FALSE);
56         }
57 }
58
59 static void update_carrier(struct connman_device *device, unsigned flags)
60 {
61         struct connman_element *netdev;
62
63         if (flags & IFF_LOWER_UP) {
64                 DBG("carrier on");
65
66                 netdev = connman_element_create(NULL);
67                 if (netdev != NULL) {
68                         netdev->type    = CONNMAN_ELEMENT_TYPE_DEVICE;
69                         netdev->subtype = CONNMAN_ELEMENT_SUBTYPE_NETWORK;
70                         netdev->index   = device->element->index;
71
72                         if (connman_element_register(netdev,
73                                                         device->element) < 0)
74                                 connman_element_unref(netdev);
75                 }
76         } else {
77                 DBG("carrier off");
78
79                 connman_element_unregister_children(device->element);
80         }
81 }
82
83 static void ethernet_newlink(unsigned short type, int index,
84                                         unsigned flags, unsigned change)
85 {
86         GSList *list;
87
88         DBG("index %d flags %ld change %ld", index, flags, change);
89
90         for (list = ethernet_list; list; list = list->next) {
91                 struct connman_device *device = list->data;
92                 struct ethernet_data *ethernet;
93
94                 ethernet = connman_device_get_data(device);
95                 if (ethernet == NULL)
96                         continue;
97
98                 if (ethernet->index != index)
99                         continue;
100
101                 if ((ethernet->flags & IFF_UP) != (flags & IFF_UP))
102                         update_power(device, flags);
103
104                 if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP))
105                         update_carrier(device, flags);
106
107                 ethernet->flags = flags;
108         }
109 }
110
111 static struct connman_rtnl ethernet_rtnl = {
112         .name           = "ethernet",
113         .newlink        = ethernet_newlink,
114 };
115
116 static int iface_up(struct ethernet_data *ethernet)
117 {
118         struct ifreq ifr;
119         int sk, err;
120
121         DBG("index %d flags %d", ethernet->index, ethernet->flags);
122
123         sk = socket(PF_INET, SOCK_DGRAM, 0);
124         if (sk < 0)
125                 return -errno;
126
127         memset(&ifr, 0, sizeof(ifr));
128         ifr.ifr_ifindex = ethernet->index;
129
130         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
131                 err = -errno;
132                 goto done;
133         }
134
135         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
136                 err = -errno;
137                 goto done;
138         }
139
140         if (ifr.ifr_flags & IFF_UP) {
141                 err = -EALREADY;
142                 goto done;
143         }
144
145         ifr.ifr_flags |= IFF_UP;
146
147         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
148                 err = -errno;
149                 goto done;
150         }
151
152         err = 0;
153
154 done:
155         close(sk);
156
157         return err;
158 }
159
160 static int iface_down(struct ethernet_data *ethernet)
161 {
162         struct ifreq ifr;
163         int sk, err;
164
165         DBG("index %d flags %d", ethernet->index, ethernet->flags);
166
167         sk = socket(PF_INET, SOCK_DGRAM, 0);
168         if (sk < 0)
169                 return -errno;
170
171         memset(&ifr, 0, sizeof(ifr));
172         ifr.ifr_ifindex = ethernet->index;
173
174         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
175                 err = -errno;
176                 goto done;
177         }
178
179         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
180                 err = -errno;
181                 goto done;
182         }
183
184         if (!(ifr.ifr_flags & IFF_UP)) {
185                 err = -EALREADY;
186                 goto done;
187         }
188
189         ifr.ifr_flags &= ~IFF_UP;
190
191         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
192                 err = -errno;
193         else
194                 err = 0;
195
196 done:
197         close(sk);
198
199         return err;
200 }
201
202 static int ethernet_probe(struct connman_device *device)
203 {
204         struct ethernet_data *ethernet;
205
206         DBG("device %p", device);
207
208         ethernet = g_try_new0(struct ethernet_data, 1);
209         if (ethernet == NULL)
210                 return -ENOMEM;
211
212         ethernet_list = g_slist_append(ethernet_list, device);
213
214         connman_device_set_data(device, ethernet);
215
216         ethernet->index = device->element->index;
217
218         connman_rtnl_send_getlink();
219
220         return 0;
221 }
222
223 static void ethernet_remove(struct connman_device *device)
224 {
225         struct ethernet_data *ethernet = connman_device_get_data(device);
226
227         DBG("device %p", device);
228
229         connman_device_set_data(device, NULL);
230
231         ethernet_list = g_slist_remove(ethernet_list, device);
232
233         g_free(ethernet);
234 }
235
236 static int ethernet_enable(struct connman_device *device)
237 {
238         struct ethernet_data *ethernet = connman_device_get_data(device);
239
240         DBG("device %p", device);
241
242         return iface_up(ethernet);
243 }
244
245 static int ethernet_disable(struct connman_device *device)
246 {
247         struct ethernet_data *ethernet = connman_device_get_data(device);
248
249         DBG("device %p", device);
250
251         return iface_down(ethernet);
252 }
253
254 static struct connman_device_driver ethernet_driver = {
255         .name           = "ethernet",
256         .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
257         .probe          = ethernet_probe,
258         .remove         = ethernet_remove,
259         .enable         = ethernet_enable,
260         .disable        = ethernet_disable,
261 };
262
263 static int ethernet_init(void)
264 {
265         int err;
266
267         err = connman_rtnl_register(&ethernet_rtnl);
268         if (err < 0)
269                 return err;
270
271         err = connman_device_driver_register(&ethernet_driver);
272         if (err < 0) {
273                 connman_rtnl_unregister(&ethernet_rtnl);
274                 return err;
275         }
276
277         return 0;
278 }
279
280 static void ethernet_exit(void)
281 {
282         connman_device_driver_unregister(&ethernet_driver);
283
284         connman_rtnl_unregister(&ethernet_rtnl);
285 }
286
287 CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
288                                                 ethernet_init, ethernet_exit)