Rename "subsystems" directory to "base"
[oonf.git] / src / generic / eth_listener / eth_listener.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <errno.h>
47 #include <linux/sockios.h>
48 #include <net/if.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <sys/types.h>
52 #include <unistd.h>
53
54 #include <oonf/oonf.h>
55 #include <oonf/libconfig/cfg_schema.h>
56 #include <oonf/libcore/oonf_subsystem.h>
57 #include <oonf/base/oonf_clock.h>
58 #include <oonf/base/oonf_layer2.h>
59 #include <oonf/base/oonf_timer.h>
60 #include <oonf/base/os_interface.h>
61
62 #include <oonf/generic/eth_listener/eth_listener.h>
63 #include <oonf/generic/eth_listener/ethtool-copy.h>
64
65 /* definitions */
66 #define LOG_ETH _eth_listener_subsystem.logging
67
68 /**
69  * Configuration object for eth listener
70  */
71 struct _eth_config {
72   /*! interval between two updates */
73   uint64_t interval;
74 };
75
76 /* prototypes */
77 static int _init(void);
78 static void _cleanup(void);
79
80 static void _cb_transmission_event(struct oonf_timer_instance *);
81 static void _cb_config_changed(void);
82
83 /* configuration */
84 static struct cfg_schema_entry _eth_entries[] = {
85   CFG_MAP_CLOCK_MIN(
86     _eth_config, interval, "interval", "60.0", "Interval between two linklayer information updates", 100),
87 };
88
89 static struct cfg_schema_section _eth_section = {
90   .type = OONF_ETH_LISTENER_SUBSYSTEM,
91   .cb_delta_handler = _cb_config_changed,
92   .entries = _eth_entries,
93   .entry_count = ARRAYSIZE(_eth_entries),
94 };
95
96 static struct _eth_config _config;
97
98 /* plugin declaration */
99 static const char *_dependencies[] = {
100   OONF_CLOCK_SUBSYSTEM,
101   OONF_LAYER2_SUBSYSTEM,
102   OONF_TIMER_SUBSYSTEM,
103   OONF_OS_INTERFACE_SUBSYSTEM,
104 };
105 static struct oonf_subsystem _eth_listener_subsystem = {
106   .name = OONF_ETH_LISTENER_SUBSYSTEM,
107   .dependencies = _dependencies,
108   .dependencies_count = ARRAYSIZE(_dependencies),
109   .descr = "OONF ethernet listener plugin",
110   .author = "Henning Rogge",
111
112   .cfg_section = &_eth_section,
113
114   .init = _init,
115   .cleanup = _cleanup,
116 };
117 DECLARE_OONF_PLUGIN(_eth_listener_subsystem);
118
119 /* timer for generating netlink requests */
120 static struct oonf_timer_class _transmission_timer_info = {
121   .name = "nl80211 listener timer",
122   .callback = _cb_transmission_event,
123   .periodic = true,
124 };
125
126 static struct oonf_timer_instance _transmission_timer = { .class = &_transmission_timer_info };
127
128 static struct oonf_layer2_origin _l2_origin = {
129   .name = "ethernet listener",
130   .priority = OONF_LAYER2_ORIGIN_UNRELIABLE,
131   .proactive = true,
132 };
133
134 static int _ioctl_sock;
135
136 static int
137 _init(void) {
138   _ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
139   if (_ioctl_sock == -1) {
140     OONF_WARN(LOG_ETH, "Could not open ioctl socket");
141     return -1;
142   }
143
144   oonf_timer_add(&_transmission_timer_info);
145   oonf_layer2_origin_add(&_l2_origin);
146
147   return 0;
148 }
149
150 static void
151 _cleanup(void) {
152   oonf_layer2_origin_remove(&_l2_origin);
153
154   oonf_timer_stop(&_transmission_timer);
155   oonf_timer_remove(&_transmission_timer_info);
156
157   close(_ioctl_sock);
158 }
159
160 /**
161  * Callback for querying ethernet status
162  * @param ptr timer instance that fired
163  */
164 static void
165 _cb_transmission_event(struct oonf_timer_instance *ptr __attribute((unused))) {
166   struct oonf_layer2_net *l2net;
167   struct os_interface *os_if;
168   struct ethtool_cmd cmd;
169   struct ifreq req;
170   int64_t ethspeed;
171   int err;
172 #ifdef OONF_LOG_DEBUG_INFO
173   struct isonumber_str ibuf;
174 #endif
175
176   avl_for_each_element(os_interface_get_tree(), os_if, _node) {
177     /* initialize ethtool command */
178     memset(&cmd, 0, sizeof(cmd));
179     cmd.cmd = ETHTOOL_GSET;
180
181     /* initialize interface request */
182     memset(&req, 0, sizeof(req));
183     req.ifr_data = (void *)&cmd;
184
185     if (os_if->base_index != os_if->index) {
186       /* get name of base interface */
187       if (if_indextoname(os_if->base_index, req.ifr_name) == NULL) {
188         /* do not use WARN, maybe the base-index is not available in this namespace */
189         OONF_DEBUG(
190           LOG_ETH, "Could not get interface name of index %u: %s (%d)", os_if->base_index, strerror(errno), errno);
191         continue;
192       }
193     }
194     else {
195       /* copy interface name directly */
196       strscpy(req.ifr_name, os_if->name, IF_NAMESIZE);
197     }
198
199     /* request ethernet information from kernel */
200     err = ioctl(_ioctl_sock, SIOCETHTOOL, &req);
201     if (err != 0) {
202       continue;
203     }
204
205     /* get ethernet linkspeed */
206     ethspeed = ethtool_cmd_speed(&cmd);
207     if (ethspeed == 0 || ethspeed == (uint16_t)-1 || ethspeed == (uint32_t)-1) {
208       /* speed is not known */
209       continue;
210     }
211     ethspeed *= 1000 * 1000;
212
213     /* layer-2 object for this interface */
214     l2net = oonf_layer2_net_add(os_if->name);
215     if (l2net == NULL) {
216       continue;
217     }
218     if (l2net->if_type == OONF_LAYER2_TYPE_UNDEFINED) {
219       l2net->if_type = OONF_LAYER2_TYPE_ETHERNET;
220     }
221
222     /* set corresponding database entries */
223     OONF_DEBUG(LOG_ETH, "Set default link speed of interface %s to %s", os_if->name,
224       isonumber_from_s64(&ibuf, ethspeed, "bit/s", 0, false));
225
226     oonf_layer2_data_set_int64(&l2net->neighdata[OONF_LAYER2_NEIGH_RX_BITRATE], &_l2_origin, ethspeed);
227     oonf_layer2_data_set_int64(&l2net->neighdata[OONF_LAYER2_NEIGH_TX_BITRATE], &_l2_origin, ethspeed);
228   }
229 }
230
231 static void
232 _cb_config_changed(void) {
233   if (cfg_schema_tobin(&_config, _eth_section.post, _eth_entries, ARRAYSIZE(_eth_entries))) {
234     OONF_WARN(LOG_ETH, "Could not convert " OONF_ETH_LISTENER_SUBSYSTEM " config to bin");
235     return;
236   }
237
238   oonf_timer_set_ext(&_transmission_timer, 1, _config.interval);
239 }