Add debug message to RTNL callback
[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         DBG("index %d flags %ld change %ld", index, flags, change);
53
54         g_static_mutex_lock(&ethernet_mutex);
55
56         for (list = ethernet_list; list; list = list->next) {
57                 struct connman_element *element = list->data;
58                 struct connman_element *netdev;
59                 struct ethernet_data *ethernet;
60
61                 ethernet = connman_element_get_data(element);
62                 if (ethernet == NULL)
63                         continue;
64
65                 if (ethernet->index != index)
66                         continue;
67
68                 if ((ethernet->flags & IFF_RUNNING) == (flags & IFF_RUNNING))
69                         continue;
70
71                 ethernet->flags = flags;
72
73                 if (ethernet->flags & IFF_RUNNING) {
74                         DBG("carrier on");
75
76                         netdev = connman_element_create(NULL);
77                         if (netdev != NULL) {
78                                 netdev->type    = CONNMAN_ELEMENT_TYPE_DEVICE;
79                                 netdev->subtype = CONNMAN_ELEMENT_SUBTYPE_NETWORK;
80                                 netdev->index   = element->index;
81
82                                 connman_element_register(netdev, element);
83                         }
84                 } else {
85                         DBG("carrier off");
86
87                         connman_element_unregister_children(element);
88                 }
89         }
90
91         g_static_mutex_unlock(&ethernet_mutex);
92 }
93
94 static struct connman_rtnl ethernet_rtnl = {
95         .name           = "ethernet",
96         .newlink        = ethernet_newlink,
97 };
98
99 static int iface_up(struct ethernet_data *ethernet)
100 {
101         struct ifreq ifr;
102         int sk, err;
103
104         DBG("index %d flags %d", ethernet->index, ethernet->flags);
105
106         sk = socket(PF_INET, SOCK_DGRAM, 0);
107         if (sk < 0)
108                 return -errno;
109
110         memset(&ifr, 0, sizeof(ifr));
111         ifr.ifr_ifindex = ethernet->index;
112
113         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
114                 err = -errno;
115                 goto done;
116         }
117
118         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
119                 err = -errno;
120                 goto done;
121         }
122
123         if (ifr.ifr_flags & IFF_UP) {
124                 err = -EALREADY;
125                 goto done;
126         }
127
128         ifr.ifr_flags |= IFF_UP;
129
130         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
131                 err = -errno;
132                 goto done;
133         }
134
135         err = 0;
136
137 done:
138         close(sk);
139
140         return err;
141 }
142
143 static int iface_down(struct ethernet_data *ethernet)
144 {
145         struct ifreq ifr;
146         int sk, err;
147
148         DBG("index %d flags %d", ethernet->index, ethernet->flags);
149
150         sk = socket(PF_INET, SOCK_DGRAM, 0);
151         if (sk < 0)
152                 return -errno;
153
154         memset(&ifr, 0, sizeof(ifr));
155         ifr.ifr_ifindex = ethernet->index;
156
157         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
158                 err = -errno;
159                 goto done;
160         }
161
162         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
163                 err = -errno;
164                 goto done;
165         }
166
167         if (!(ifr.ifr_flags & IFF_UP)) {
168                 err = -EALREADY;
169                 goto done;
170         }
171
172         ifr.ifr_flags &= ~IFF_UP;
173
174         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
175                 err = -errno;
176         else
177                 err = 0;
178
179 done:
180         close(sk);
181
182         return err;
183 }
184
185 static int ethernet_probe(struct connman_element *element)
186 {
187         struct ethernet_data *ethernet;
188
189         DBG("element %p name %s", element, element->name);
190
191         ethernet = g_try_new0(struct ethernet_data, 1);
192         if (ethernet == NULL)
193                 return -ENOMEM;
194
195         g_static_mutex_lock(&ethernet_mutex);
196         ethernet_list = g_slist_append(ethernet_list, element);
197         g_static_mutex_unlock(&ethernet_mutex);
198
199         connman_element_set_data(element, ethernet);
200
201         ethernet->index = element->index;
202
203         iface_up(ethernet);
204
205         connman_rtnl_send_getlink();
206
207         return 0;
208 }
209
210 static void ethernet_remove(struct connman_element *element)
211 {
212         struct ethernet_data *ethernet = connman_element_get_data(element);
213
214         DBG("element %p name %s", element, element->name);
215
216         connman_element_set_data(element, NULL);
217
218         iface_down(ethernet);
219
220         g_static_mutex_lock(&ethernet_mutex);
221         ethernet_list = g_slist_remove(ethernet_list, element);
222         g_static_mutex_unlock(&ethernet_mutex);
223
224         g_free(ethernet);
225 }
226
227 static struct connman_driver ethernet_driver = {
228         .name           = "ethernet",
229         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
230         .subtype        = CONNMAN_ELEMENT_SUBTYPE_ETHERNET,
231         .probe          = ethernet_probe,
232         .remove         = ethernet_remove,
233 };
234
235 static int ethernet_init(void)
236 {
237         int err;
238
239         err = connman_rtnl_register(&ethernet_rtnl);
240         if (err < 0)
241                 return err;
242
243         err = connman_driver_register(&ethernet_driver);
244         if (err < 0) {
245                 connman_rtnl_unregister(&ethernet_rtnl);
246                 return err;
247         }
248
249         return 0;
250 }
251
252 static void ethernet_exit(void)
253 {
254         connman_driver_unregister(&ethernet_driver);
255
256         connman_rtnl_unregister(&ethernet_rtnl);
257 }
258
259 CONNMAN_PLUGIN_DEFINE("ethernet", "Ethernet interface plugin", VERSION,
260                                                 ethernet_init, ethernet_exit)